diff --git a/app/build.gradle b/app/build.gradle
index bf740c82..7f0c4574 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -111,6 +111,7 @@ dependencies {
implementation project(":modules:ui-customize-plates")
implementation project(":modules:common-ui-components-workout-editor")
implementation project(":modules:stopwatch")
+ implementation project(":modules:ui-customize-barbells")
implementation Deps.Kotlin.coroutinesAndroid
kapt Deps.Android.Lifecycle.compiler
diff --git a/app/src/main/java/com/ankitsuda/rebound/ui/navigation/AppNavigation.kt b/app/src/main/java/com/ankitsuda/rebound/ui/navigation/AppNavigation.kt
index e2ddaf22..b6e3ec2a 100644
--- a/app/src/main/java/com/ankitsuda/rebound/ui/navigation/AppNavigation.kt
+++ b/app/src/main/java/com/ankitsuda/rebound/ui/navigation/AppNavigation.kt
@@ -27,6 +27,8 @@ import com.ankitsuda.navigation.*
import com.ankitsuda.rebound.ui.calendar.CalendarScreen
import com.ankitsuda.rebound.ui.components.workouteditor.supersetselector.SupersetSelectorBottomSheet
import com.ankitsuda.rebound.ui.create_exercise.CreateExerciseScreen
+import com.ankitsuda.rebound.ui.customizebarbells.CustomizeBarbellsScreen
+import com.ankitsuda.rebound.ui.customizebarbells.edit.BarbellEditBottomSheet
import com.ankitsuda.rebound.ui.customizeplates.CustomizePlatesScreen
import com.ankitsuda.rebound.ui.customizeplates.edit.PlateEditBottomSheet
import com.ankitsuda.rebound.ui.exercise_details.ExerciseDetailScreen
@@ -61,9 +63,7 @@ import com.google.accompanist.navigation.material.ExperimentalMaterialNavigation
import kotlinx.coroutines.InternalCoroutinesApi
@OptIn(
- InternalCoroutinesApi::class,
- ExperimentalMaterialNavigationApi::class,
- androidx.compose.animation.ExperimentalAnimationApi::class
+ ExperimentalAnimationApi::class
)
@Composable
internal fun AppNavigation(
@@ -190,6 +190,7 @@ private fun NavGraphBuilder.addMoreRoot(navController: NavController) {
addPartMeasurementsScreen(navController)
addSettingsScreen(navController)
addCustomizePlatesScreen(navController)
+ addCustomizeBarbellsScreen(navController)
addPersonalizationScreen(navController)
addMainColorsPersonalizationScreen(navController)
addShapesPersonalizationScreen(navController)
@@ -204,6 +205,7 @@ private fun NavGraphBuilder.addMoreRoot(navController: NavController) {
addAddPartMeasurementBottomSheet(navController)
addPlateEditBottomSheet(navController)
+ addBarbellEditBottomSheet(navController)
}
}
@@ -293,6 +295,12 @@ private fun NavGraphBuilder.addCustomizePlatesScreen(navController: NavControlle
}
}
+private fun NavGraphBuilder.addCustomizeBarbellsScreen(navController: NavController) {
+ composableScreen(LeafScreen.Barbells()) {
+ CustomizeBarbellsScreen()
+ }
+}
+
private fun NavGraphBuilder.addPersonalizationScreen(navController: NavController) {
composableScreen(LeafScreen.Personalization()) {
PersonalizationScreen(navController)
@@ -383,6 +391,12 @@ private fun NavGraphBuilder.addPlateEditBottomSheet(navController: NavController
}
}
+private fun NavGraphBuilder.addBarbellEditBottomSheet(navController: NavController) {
+ bottomSheetScreen(LeafScreen.BarbellEdit()) {
+ BarbellEditBottomSheet()
+ }
+}
+
private fun NavGraphBuilder.addTemplatesFolderEditBottomSheet(navController: NavController) {
bottomSheetScreen(LeafScreen.TemplatesFolderEdit()) {
TemplatesFolderEditBottomSheet()
diff --git a/modules/base-android/src/main/java/com/ankitsuda/base/util/Utils.kt b/modules/base-android/src/main/java/com/ankitsuda/base/util/Utils.kt
index 19a72d7e..c7ffff18 100644
--- a/modules/base-android/src/main/java/com/ankitsuda/base/util/Utils.kt
+++ b/modules/base-android/src/main/java/com/ankitsuda/base/util/Utils.kt
@@ -38,6 +38,7 @@ fun Double.fromKmToMiles() = this * MILES_IN_KM
fun Double.fromKgToLbsReadable() = DecimalFormat("#.##").format(fromKgToLbs())
fun Double.kgToReadable() = DecimalFormat("#.##").format(this)
+fun Double.lbsToReadable() = kgToReadable()
fun Double.fromKmToMilesReadable(): String = DecimalFormat("#.###").format(fromKmToMiles())
fun Double.kmToReadable(): String = DecimalFormat("#.###").format(this)
\ No newline at end of file
diff --git a/modules/common-ui-compose/src/main/java/com/ankitsuda/common/compose/Utils.kt b/modules/common-ui-compose/src/main/java/com/ankitsuda/common/compose/Utils.kt
index 45207ad2..e049f6fd 100644
--- a/modules/common-ui-compose/src/main/java/com/ankitsuda/common/compose/Utils.kt
+++ b/modules/common-ui-compose/src/main/java/com/ankitsuda/common/compose/Utils.kt
@@ -34,7 +34,7 @@ fun Double?.kgToUserPrefStr(
" "
} else {
""
- } + userPrefWeightUnitStr()
+ } + userPrefWeightUnitStr(weightUnit = weightUnit)
} else {
""
}
@@ -64,7 +64,10 @@ fun WeightUnit.localizedStr(case: Int = 2): String = when (this) {
* @param case 0=Pascal Case, 1=Upper Case, 2=Lower Case
*/
@Composable
-fun userPrefWeightUnitStr(case: Int = 2): String = LocalAppSettings.current.weightUnit.localizedStr(case = case)
+fun userPrefWeightUnitStr(
+ case: Int = 2,
+ weightUnit: WeightUnit = LocalAppSettings.current.weightUnit
+): String = weightUnit.localizedStr(case = case)
@Composable
fun Double?.kmToUserPrefStr(
diff --git a/modules/common-ui-icons/src/main/java/com/ankitsuda/rebound/ui/icons/Barbell.kt b/modules/common-ui-icons/src/main/java/com/ankitsuda/rebound/ui/icons/Barbell.kt
new file mode 100644
index 00000000..2cdba60e
--- /dev/null
+++ b/modules/common-ui-icons/src/main/java/com/ankitsuda/rebound/ui/icons/Barbell.kt
@@ -0,0 +1,56 @@
+package com.ankitsuda.rebound.ui.icons
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.unit.dp
+
+public val Icons.Filled.Barbell: ImageVector
+ get() {
+ if (_barbell != null) {
+ return _barbell!!
+ }
+ _barbell = Builder(name = "Barbell", defaultWidth = 24.0.dp, defaultHeight = 24.0.dp,
+ viewportWidth = 24.0f, viewportHeight = 24.0f).apply {
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(4.052f, 2.6378f)
+ lineToRelative(17.3101f, 17.3101f)
+ lineToRelative(-1.4142f, 1.4142f)
+ lineToRelative(-17.3101f, -17.3101f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(8.0628f, 3.8201f)
+ lineToRelative(1.731f, 1.731f)
+ lineToRelative(-4.2426f, 4.2426f)
+ lineToRelative(-1.731f, -1.731f)
+ close()
+ }
+ path(fill = SolidColor(Color(0xFF000000)), stroke = null, strokeLineWidth = 0.0f,
+ strokeLineCap = Butt, strokeLineJoin = Miter, strokeLineMiter = 4.0f,
+ pathFillType = NonZero) {
+ moveTo(18.4488f, 14.2062f)
+ lineToRelative(1.731f, 1.731f)
+ lineToRelative(-4.2426f, 4.2426f)
+ lineToRelative(-1.731f, -1.731f)
+ close()
+ }
+ }
+ .build()
+ return _barbell!!
+ }
+
+private var _barbell: ImageVector? = null
diff --git a/modules/common-ui-resources/src/main/res/values/strings.xml b/modules/common-ui-resources/src/main/res/values/strings.xml
index e15fd1ce..a38f2da0 100644
--- a/modules/common-ui-resources/src/main/res/values/strings.xml
+++ b/modules/common-ui-resources/src/main/res/values/strings.xml
@@ -146,6 +146,7 @@
Perform Again
Edit session
Customize barbell plates
+ Customize barbells
Defaults
Weight Unit
Metric (kg)
@@ -212,6 +213,8 @@
Set Keyboard Demo
Plate
Weight
+ Weight (kg)
+ Weight (lbs)
Color
Height multiplier
Width multiplier
@@ -252,4 +255,7 @@
Start time
End time
Start time is longer than end time
+ Barbells
+ Barbell
+ Add barbell
\ No newline at end of file
diff --git a/modules/core-data/schemas/com.ankitsuda.rebound.data.db.AppDatabase/1.json b/modules/core-data/schemas/com.ankitsuda.rebound.data.db.AppDatabase/1.json
index e024930c..f2a35d12 100644
--- a/modules/core-data/schemas/com.ankitsuda.rebound.data.db.AppDatabase/1.json
+++ b/modules/core-data/schemas/com.ankitsuda.rebound.data.db.AppDatabase/1.json
@@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 1,
- "identityHash": "78f1033c7025b8448214463c96e35563",
+ "identityHash": "68617553ff75764b3f0264b855b8cd96",
"entities": [
{
"tableName": "body_parts",
@@ -841,12 +841,68 @@
},
"indices": [],
"foreignKeys": []
+ },
+ {
+ "tableName": "barbells",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT, `weight_kg` REAL, `weight_lbs` REAL, `is_active` INTEGER, `created_at` INTEGER, `update_at` INTEGER, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "weightKg",
+ "columnName": "weight_kg",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "weightLbs",
+ "columnName": "weight_lbs",
+ "affinity": "REAL",
+ "notNull": false
+ },
+ {
+ "fieldPath": "isActive",
+ "columnName": "is_active",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "createdAt",
+ "columnName": "created_at",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "updatedAt",
+ "columnName": "update_at",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "columnNames": [
+ "id"
+ ],
+ "autoGenerate": false
+ },
+ "indices": [],
+ "foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
- "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '78f1033c7025b8448214463c96e35563')"
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '68617553ff75764b3f0264b855b8cd96')"
]
}
}
\ No newline at end of file
diff --git a/modules/core-data/src/main/java/com/ankitsuda/rebound/data/db/AppDatabase.kt b/modules/core-data/src/main/java/com/ankitsuda/rebound/data/db/AppDatabase.kt
index 63a0a672..fc2a139b 100644
--- a/modules/core-data/src/main/java/com/ankitsuda/rebound/data/db/AppDatabase.kt
+++ b/modules/core-data/src/main/java/com/ankitsuda/rebound/data/db/AppDatabase.kt
@@ -39,6 +39,7 @@ import kotlinx.coroutines.launch
ThemePreset::class,
ExerciseSetGroupNote::class,
WorkoutTemplatesFolder::class,
+ Barbell::class
],
version = 1,
exportSchema = true
@@ -54,6 +55,7 @@ abstract class AppDatabase : RoomDatabase() {
abstract fun platesDao(): PlatesDao
abstract fun themePresetsDao(): ThemePresetsDao
abstract fun workoutFoldersFoldersDao(): WorkoutFoldersFoldersDao
+ abstract fun barbellsDao(): BarbellsDao
companion object {
@Volatile
diff --git a/modules/core-data/src/main/java/com/ankitsuda/rebound/data/db/DatabaseModule.kt b/modules/core-data/src/main/java/com/ankitsuda/rebound/data/db/DatabaseModule.kt
index d6acc895..f8a2f991 100644
--- a/modules/core-data/src/main/java/com/ankitsuda/rebound/data/db/DatabaseModule.kt
+++ b/modules/core-data/src/main/java/com/ankitsuda/rebound/data/db/DatabaseModule.kt
@@ -62,4 +62,7 @@ class DatabaseModule {
@Provides
fun provideWorkoutFoldersFoldersDao(db: AppDatabase) = db.workoutFoldersFoldersDao()
+ @Provides
+ fun provideBarbellsDao(db: AppDatabase) = db.barbellsDao()
+
}
diff --git a/modules/core-data/src/main/java/com/ankitsuda/rebound/data/db/daos/BarbellsDao.kt b/modules/core-data/src/main/java/com/ankitsuda/rebound/data/db/daos/BarbellsDao.kt
new file mode 100644
index 00000000..0731b09d
--- /dev/null
+++ b/modules/core-data/src/main/java/com/ankitsuda/rebound/data/db/daos/BarbellsDao.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2022 Ankit Suda.
+ *
+ * Licensed under the GNU General Public License v3
+ *
+ * This is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ */
+
+package com.ankitsuda.rebound.data.db.daos
+
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import com.ankitsuda.rebound.domain.WeightUnit
+import com.ankitsuda.rebound.domain.entities.Muscle
+import com.ankitsuda.rebound.domain.entities.Barbell
+import kotlinx.coroutines.flow.Flow
+
+@Dao
+interface BarbellsDao {
+
+ @Query("SELECT * FROM barbells WHERE id = :id")
+ fun getBarbell(id: String): Flow
+
+ @Query("SELECT * FROM barbells ORDER BY name")
+ fun getBarbells(): Flow>
+
+ @Query("SELECT * FROM barbells WHERE is_active = 1 ORDER BY name")
+ fun getActiveBarbells(): Flow>
+
+ @Query("UPDATE barbells SET is_active = :isActive WHERE id = :barbellId")
+ suspend fun updateIsActive(barbellId: String, isActive: Boolean)
+
+ @Insert
+ suspend fun insertBarbells(barbells: List)
+
+ @Query("DELETE FROM barbells WHERE id = :barbellId")
+ suspend fun deleteBarbell(barbellId: String)
+
+ @Insert(onConflict = OnConflictStrategy.REPLACE)
+ suspend fun insertBarbell(barbell: Barbell)
+
+}
\ No newline at end of file
diff --git a/modules/core-data/src/main/java/com/ankitsuda/rebound/data/repositories/BarbellsRepository.kt b/modules/core-data/src/main/java/com/ankitsuda/rebound/data/repositories/BarbellsRepository.kt
new file mode 100644
index 00000000..034eb375
--- /dev/null
+++ b/modules/core-data/src/main/java/com/ankitsuda/rebound/data/repositories/BarbellsRepository.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2022 Ankit Suda.
+ *
+ * Licensed under the GNU General Public License v3
+ *
+ * This is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ */
+
+package com.ankitsuda.rebound.data.repositories
+
+import com.ankitsuda.rebound.data.db.daos.BarbellsDao
+import com.ankitsuda.rebound.domain.WeightUnit
+import com.ankitsuda.rebound.domain.entities.Barbell
+import javax.inject.Inject
+
+class BarbellsRepository @Inject constructor(private val barbellsDao: BarbellsDao) {
+
+ fun getBarbells() = barbellsDao.getBarbells()
+
+ fun getActiveBarbells() = barbellsDao.getActiveBarbells()
+
+ fun getBarbell(barbellId: String) = barbellsDao.getBarbell(barbellId)
+
+ suspend fun upsertBarbell(barbell: Barbell) = barbellsDao.insertBarbell(barbell)
+
+ suspend fun updateIsActive(barbellId: String, isActive: Boolean) {
+ barbellsDao.updateIsActive(barbellId, isActive)
+ }
+
+ suspend fun deleteBarbell(barbellId: String) {
+ barbellsDao.deleteBarbell(barbellId)
+ }
+
+}
\ No newline at end of file
diff --git a/modules/core-data/src/main/java/com/ankitsuda/rebound/data/repositories/RepositoriesModule.kt b/modules/core-data/src/main/java/com/ankitsuda/rebound/data/repositories/RepositoriesModule.kt
index 8b812f6d..4c82f833 100644
--- a/modules/core-data/src/main/java/com/ankitsuda/rebound/data/repositories/RepositoriesModule.kt
+++ b/modules/core-data/src/main/java/com/ankitsuda/rebound/data/repositories/RepositoriesModule.kt
@@ -68,4 +68,11 @@ class RepositoriesModule {
presetsDao: ThemePresetsDao
) =
ThemePresetsRepository(presetsDao)
+
+ @Singleton
+ @Provides
+ fun provideBarbellsRepository(
+ barbellsDao: BarbellsDao
+ ) =
+ BarbellsRepository(barbellsDao)
}
\ No newline at end of file
diff --git a/modules/domain/src/main/java/com/ankitsuda/rebound/domain/entities/Barbell.kt b/modules/domain/src/main/java/com/ankitsuda/rebound/domain/entities/Barbell.kt
new file mode 100644
index 00000000..93129d4b
--- /dev/null
+++ b/modules/domain/src/main/java/com/ankitsuda/rebound/domain/entities/Barbell.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2022 Ankit Suda.
+ *
+ * Licensed under the GNU General Public License v3
+ *
+ * This is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ */
+
+package com.ankitsuda.rebound.domain.entities
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.ankitsuda.rebound.domain.ExerciseCategory
+import com.ankitsuda.rebound.domain.WeightUnit
+import java.time.LocalDateTime
+import java.util.*
+
+@Entity(tableName = "barbells")
+data class Barbell(
+ @PrimaryKey(autoGenerate = false)
+ @ColumnInfo(name = "id")
+ val id: String,
+
+ @ColumnInfo(name = "name")
+ var name: String? = null,
+
+ @ColumnInfo(name = "weight_kg")
+ var weightKg: Double? = null,
+
+ @ColumnInfo(name = "weight_lbs")
+ var weightLbs: Double? = null,
+
+ @ColumnInfo(name = "is_active")
+ var isActive: Boolean? = null,
+
+ @ColumnInfo(name = "created_at")
+ var createdAt: LocalDateTime? = null,
+ @ColumnInfo(name = "update_at")
+ var updatedAt: LocalDateTime? = null,
+)
\ No newline at end of file
diff --git a/modules/navigation/src/main/java/com/ankitsuda/navigation/Screens.kt b/modules/navigation/src/main/java/com/ankitsuda/navigation/Screens.kt
index c46bb3f0..2088f6ac 100644
--- a/modules/navigation/src/main/java/com/ankitsuda/navigation/Screens.kt
+++ b/modules/navigation/src/main/java/com/ankitsuda/navigation/Screens.kt
@@ -45,6 +45,7 @@ const val FOLDER_ID_KEY = "folder_id"
const val SUPERSET_ID_KEY = "superset_id"
const val JUNCTION_ID_KEY = "junction_id"
const val ENTRY_ID_KEY = "entry_id"
+const val BARBELL_ID_KEY = "barbell_id"
const val RESULT_EXERCISES_SCREEN_EXERCISE_ID = "result_exercises_screen_exercise_id"
const val RESULT_SUPERSET_SELECTOR_SUPERSET_ID_KEY = "result_superset_selector_superset_id"
@@ -225,6 +226,38 @@ sealed class LeafScreen(
}
}
+ data class Barbells(
+ override val route: String = "barbells",
+ override val root: TabRootScreen = TabRootScreen.MoreTab
+ ) : LeafScreen(route, root)
+
+ data class BarbellEdit(
+ override val route: String = "barbells/edit?$BARBELL_ID_KEY={$BARBELL_ID_KEY}",
+ override val root: TabRootScreen = TabRootScreen.MoreTab
+ ) : LeafScreen(
+ route = route,
+ root = root,
+ arguments = listOf(
+ navArgument(BARBELL_ID_KEY) {
+ type = NavType.StringType
+ nullable = true
+ },
+ ),
+ ) {
+ companion object {
+ fun createRoute(
+ plateId: String? = null,
+ root: TabRootScreen = TabRootScreen.MoreTab
+ ): String {
+ var str = "${root.route}/barbells/edit"
+ plateId?.let {
+ str += "?${BARBELL_ID_KEY}=$it"
+ }
+ return str;
+ }
+ }
+ }
+
data class Personalization(
override val route: String = "personalization",
override val root: TabRootScreen = TabRootScreen.MoreTab
diff --git a/modules/ui-customize-barbells/build.gradle b/modules/ui-customize-barbells/build.gradle
new file mode 100644
index 00000000..83e0b3ae
--- /dev/null
+++ b/modules/ui-customize-barbells/build.gradle
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2022 Ankit Suda.
+ *
+ * Licensed under the GNU General Public License v3
+ *
+ * This is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ */
+
+import com.ankitsuda.rebound.buildSrc.App
+import com.ankitsuda.rebound.buildSrc.Deps
+
+plugins {
+ id "com.android.library"
+ id "dagger.hilt.android.plugin"
+ id "kotlin-android"
+ id "kotlin-kapt"
+ id "kotlin-parcelize"
+}
+
+android {
+ compileSdkVersion App.compileSdkVersion
+
+ defaultConfig {
+ minSdkVersion App.minSdkVersion
+ }
+
+ compileOptions {
+ coreLibraryDesugaringEnabled true
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ buildFeatures {
+ compose = true
+ }
+
+ composeOptions {
+ kotlinCompilerExtensionVersion Deps.Android.Compose.compilerVersion
+ }
+ namespace 'com.ankitsuda.rebound.ui.customizebarbells'
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ api project(":modules:common-ui-compose")
+ api project(":modules:common-ui-theme")
+ api project(":modules:common-ui-components")
+ api project(":modules:core-data")
+ api project(":modules:navigation")
+
+ implementation Deps.Dagger.hilt
+ kapt Deps.Dagger.hiltCompiler
+
+ coreLibraryDesugaring Deps.Android.desugaring
+}
diff --git a/modules/ui-customize-barbells/src/main/java/com/ankitsuda/rebound/ui/customizebarbells/CustomizeBarbellsScreen.kt b/modules/ui-customize-barbells/src/main/java/com/ankitsuda/rebound/ui/customizebarbells/CustomizeBarbellsScreen.kt
new file mode 100644
index 00000000..58a7650b
--- /dev/null
+++ b/modules/ui-customize-barbells/src/main/java/com/ankitsuda/rebound/ui/customizebarbells/CustomizeBarbellsScreen.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2022 Ankit Suda.
+ *
+ * Licensed under the GNU General Public License v3
+ *
+ * This is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ */
+
+package com.ankitsuda.rebound.ui.customizebarbells
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material.*
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Add
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.hilt.navigation.compose.hiltViewModel
+import com.ankitsuda.navigation.LeafScreen
+import com.ankitsuda.navigation.LocalNavigator
+import com.ankitsuda.navigation.Navigator
+import com.ankitsuda.rebound.domain.entities.Barbell
+import com.ankitsuda.rebound.ui.components.TopBar2
+import com.ankitsuda.rebound.ui.components.TopBarBackIconButton
+import com.ankitsuda.rebound.ui.components.TopBarIconButton
+import com.ankitsuda.rebound.ui.customizebarbells.components.BarbellListItemComponent
+import com.ankitsuda.rebound.ui.theme.ReboundTheme
+import me.onebone.toolbar.CollapsingToolbarScaffold
+import me.onebone.toolbar.ScrollStrategy
+import me.onebone.toolbar.rememberCollapsingToolbarScaffoldState
+
+@Composable
+fun CustomizeBarbellsScreen(
+ navigator: Navigator = LocalNavigator.current,
+ viewModel: CustomizeBarbellsScreenViewModel = hiltViewModel()
+) {
+ val barbells by viewModel.barbells.collectAsState(initial = null)
+
+ CustomizeBarbellsScreenLayout(
+ navigator = navigator,
+ barbells = barbells,
+ onUpdateIsActive = { id, isActive ->
+ viewModel.updateIsActive(id, isActive)
+ },
+ onDeleteBarbell = { id ->
+ viewModel.deleteBarbell(id)
+ }
+ )
+
+}
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+private fun CustomizeBarbellsScreenLayout(
+ navigator: Navigator,
+ barbells: List?,
+ onUpdateIsActive: (id: String, isActive: Boolean) -> Unit,
+ onDeleteBarbell: (id: String) -> Unit,
+) {
+ val collapsingState = rememberCollapsingToolbarScaffoldState()
+
+ CollapsingToolbarScaffold(
+ modifier = Modifier.background(ReboundTheme.colors.background),
+ scrollStrategy = ScrollStrategy.EnterAlwaysCollapsed,
+ state = collapsingState,
+ toolbar = {
+ TopBar2(
+ title = stringResource(id = R.string.barbells),
+ toolbarState = collapsingState.toolbarState,
+ navigationIcon = {
+ TopBarBackIconButton {
+ navigator.goBack()
+ }
+ },
+ actions = {
+ TopBarIconButton(
+ icon = Icons.Outlined.Add,
+ title = stringResource(id = R.string.add_barbell),
+ onClick = {
+ navigator.navigate(LeafScreen.BarbellEdit.createRoute())
+ }
+ )
+ }
+ )
+ }) {
+ LazyColumn(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(ReboundTheme.colors.background),
+ ) {
+ if (barbells != null) {
+ items(items = barbells, key = { it.id }) {
+ BarbellListItemComponent(
+ modifier = Modifier
+ .fillMaxWidth()
+ .animateItemPlacement(),
+ barbell = it,
+ onUpdateIsActive = { newIsActive ->
+ onUpdateIsActive(it.id, newIsActive)
+ },
+ onEditBarbell = {
+ navigator.navigate(LeafScreen.BarbellEdit.createRoute(it.id))
+ },
+ onDeleteBarbell = {
+ onDeleteBarbell(it.id)
+ }
+ )
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/ui-customize-barbells/src/main/java/com/ankitsuda/rebound/ui/customizebarbells/CustomizeBarbellsScreenViewModel.kt b/modules/ui-customize-barbells/src/main/java/com/ankitsuda/rebound/ui/customizebarbells/CustomizeBarbellsScreenViewModel.kt
new file mode 100644
index 00000000..56fdc83c
--- /dev/null
+++ b/modules/ui-customize-barbells/src/main/java/com/ankitsuda/rebound/ui/customizebarbells/CustomizeBarbellsScreenViewModel.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2022 Ankit Suda.
+ *
+ * Licensed under the GNU General Public License v3
+ *
+ * This is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ */
+
+package com.ankitsuda.rebound.ui.customizebarbells
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.ankitsuda.base.utils.extensions.shareWhileObserved
+import com.ankitsuda.rebound.data.repositories.BarbellsRepository
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+import javax.inject.Inject
+
+@HiltViewModel
+class CustomizeBarbellsScreenViewModel @Inject constructor(
+ private val barbellsRepository: BarbellsRepository
+) : ViewModel() {
+ val barbells = barbellsRepository.getBarbells()
+ .shareWhileObserved(viewModelScope)
+
+ fun updateIsActive(barbellId: String, isActive: Boolean) {
+ viewModelScope.launch {
+ barbellsRepository.updateIsActive(barbellId, isActive)
+ }
+ }
+
+ fun deleteBarbell(barbellId: String) {
+ viewModelScope.launch {
+ barbellsRepository.deleteBarbell(barbellId)
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/ui-customize-barbells/src/main/java/com/ankitsuda/rebound/ui/customizebarbells/components/BarbellListItemComponent.kt b/modules/ui-customize-barbells/src/main/java/com/ankitsuda/rebound/ui/customizebarbells/components/BarbellListItemComponent.kt
new file mode 100644
index 00000000..9a16bd69
--- /dev/null
+++ b/modules/ui-customize-barbells/src/main/java/com/ankitsuda/rebound/ui/customizebarbells/components/BarbellListItemComponent.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2022 Ankit Suda.
+ *
+ * Licensed under the GNU General Public License v3
+ *
+ * This is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ */
+
+package com.ankitsuda.rebound.ui.customizebarbells.components
+
+import androidx.compose.foundation.layout.*
+import androidx.compose.material.Icon
+import androidx.compose.material.IconButton
+import androidx.compose.material.Switch
+import androidx.compose.material.Text
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.MoreVert
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import com.ankitsuda.base.util.kgToReadable
+import com.ankitsuda.base.util.lbsToReadable
+import com.ankitsuda.base.util.toReadableString
+import com.ankitsuda.common.compose.kgToUserPrefStr
+import com.ankitsuda.common.compose.localizedStr
+import com.ankitsuda.rebound.domain.WeightUnit
+import com.ankitsuda.rebound.domain.entities.Barbell
+import com.ankitsuda.rebound.ui.components.RSpacer
+import com.ankitsuda.rebound.ui.customizebarbells.R
+import com.ankitsuda.rebound.ui.theme.ReboundTheme
+
+@Composable
+internal fun BarbellListItemComponent(
+ modifier: Modifier = Modifier,
+ barbell: Barbell,
+ onUpdateIsActive: (Boolean) -> Unit,
+ onDeleteBarbell: () -> Unit,
+ onEditBarbell: () -> Unit
+) {
+ var isActive by remember(barbell.isActive) {
+ mutableStateOf(barbell.isActive ?: false)
+ }
+
+ var menuExpanded by remember {
+ mutableStateOf(false)
+ }
+
+ fun updateIsActive(active: Boolean) {
+ isActive = active
+ onUpdateIsActive(active)
+ }
+
+ Box(
+ modifier = modifier
+ ) {
+ Row(
+ modifier = Modifier
+ .padding(start = 24.dp, end = 8.dp, bottom = 12.dp, top = 12.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Column(
+ modifier = Modifier.weight(1f),
+ verticalArrangement = Arrangement.spacedBy(4.dp)
+ ) {
+ Text(
+ text = barbell.name ?: "",
+ style = ReboundTheme.typography.body1,
+ color = ReboundTheme.colors.onBackground
+ )
+ Text(
+ text = "${
+ barbell.weightKg?.kgToReadable()
+ } ${WeightUnit.KG.localizedStr()} / ${
+ barbell.weightLbs?.lbsToReadable()
+ } ${WeightUnit.LBS.localizedStr()}",
+ style = ReboundTheme.typography.body2,
+ color = ReboundTheme.colors.onBackground.copy(alpha = 0.75f)
+ )
+ }
+ Switch(
+ checked = isActive,
+ onCheckedChange = {
+ updateIsActive(it)
+ }
+ )
+ RSpacer(space = 4.dp)
+ IconButton(onClick = {
+ menuExpanded = true
+ }) {
+ Icon(
+ imageVector = Icons.Outlined.MoreVert,
+ contentDescription = stringResource(R.string.more),
+ tint = ReboundTheme.colors.onBackground
+ )
+
+ if (menuExpanded) {
+ BarbellMenuComponent(
+ expanded = menuExpanded,
+ onDismissRequest = {
+ menuExpanded = false
+ },
+ onEditBarbell = onEditBarbell,
+ onDeleteBarbell = onDeleteBarbell
+ )
+ }
+
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/modules/ui-customize-barbells/src/main/java/com/ankitsuda/rebound/ui/customizebarbells/components/BarbellMenuComponent.kt b/modules/ui-customize-barbells/src/main/java/com/ankitsuda/rebound/ui/customizebarbells/components/BarbellMenuComponent.kt
new file mode 100644
index 00000000..3830d9e8
--- /dev/null
+++ b/modules/ui-customize-barbells/src/main/java/com/ankitsuda/rebound/ui/customizebarbells/components/BarbellMenuComponent.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2022 Ankit Suda.
+ *
+ * Licensed under the GNU General Public License v3
+ *
+ * This is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ */
+
+package com.ankitsuda.rebound.ui.customizebarbells.components
+
+import androidx.compose.material.DropdownMenu
+import androidx.compose.material.DropdownMenuItem
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import com.ankitsuda.rebound.ui.customizebarbells.R
+
+@Composable
+internal fun BarbellMenuComponent(
+ modifier: Modifier = Modifier,
+ expanded: Boolean,
+ onDismissRequest: () -> Unit,
+ onEditBarbell: () -> Unit,
+ onDeleteBarbell: () -> Unit,
+) {
+
+ DropdownMenu(
+ modifier = modifier,
+ expanded = expanded,
+ onDismissRequest = onDismissRequest
+ ) {
+ DropdownMenuItem(onClick = {
+ onDismissRequest()
+ onEditBarbell()
+ }) {
+ Text(stringResource(R.string.edit))
+ }
+ DropdownMenuItem(onClick = {
+ onDismissRequest()
+ onDeleteBarbell()
+ }) {
+ Text(stringResource(id = R.string.delete))
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/ui-customize-barbells/src/main/java/com/ankitsuda/rebound/ui/customizebarbells/edit/BarbellEditBottomSheet.kt b/modules/ui-customize-barbells/src/main/java/com/ankitsuda/rebound/ui/customizebarbells/edit/BarbellEditBottomSheet.kt
new file mode 100644
index 00000000..f3f65569
--- /dev/null
+++ b/modules/ui-customize-barbells/src/main/java/com/ankitsuda/rebound/ui/customizebarbells/edit/BarbellEditBottomSheet.kt
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2022 Ankit Suda.
+ *
+ * Licensed under the GNU General Public License v3
+ *
+ * This is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ */
+
+package com.ankitsuda.rebound.ui.customizebarbells.edit
+
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalSoftwareKeyboardController
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardCapitalization
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.unit.dp
+import androidx.hilt.navigation.compose.hiltViewModel
+import com.ankitsuda.navigation.LocalNavigator
+import com.ankitsuda.navigation.Navigator
+import com.ankitsuda.rebound.ui.components.*
+import com.ankitsuda.rebound.ui.customizebarbells.R
+
+@Composable
+fun BarbellEditBottomSheet(
+ navigator: Navigator = LocalNavigator.current,
+ viewModel: BarbellEditBottomSheetViewModel = hiltViewModel()
+) {
+ val nameFieldValue by viewModel.nameFieldValue.collectAsState()
+ val weightKgFieldValue by viewModel.weightKgFieldValue.collectAsState()
+ val weightLbsFieldValue by viewModel.weightLbsFieldValue.collectAsState()
+ val isUpdate = viewModel.isUpdate
+
+ BarbellEditBottomSheetLayout(
+ nameFieldValue = nameFieldValue,
+ weightKgFieldValue = weightKgFieldValue,
+ weightLbsFieldValue = weightLbsFieldValue,
+ isUpdate = isUpdate,
+ isSaveBtnEnabled = nameFieldValue.isNotBlank() &&
+ weightKgFieldValue.isNotBlank() &&
+ weightLbsFieldValue.isNotBlank(),
+ onChangeNameFieldValue = {
+ viewModel.updateNameFieldValue(it)
+ },
+ onChangeWeightKgFieldValue = {
+ viewModel.updateWeightKgFieldValue(it)
+ },
+ onChangeWeightLbsFieldValue = {
+ viewModel.updateWeightLbsFieldValue(it)
+ },
+ onDelete = {
+ viewModel.deleteBarbell {
+ navigator.goBack()
+ }
+ },
+ onSave = {
+ viewModel.saveBarbell {
+ navigator.goBack()
+ }
+ }
+ )
+}
+
+@OptIn(ExperimentalComposeUiApi::class)
+@Composable
+private fun BarbellEditBottomSheetLayout(
+ nameFieldValue: String,
+ weightKgFieldValue: String,
+ weightLbsFieldValue: String,
+ isUpdate: Boolean,
+ isSaveBtnEnabled: Boolean,
+ onChangeNameFieldValue: (String) -> Unit,
+ onChangeWeightKgFieldValue: (String) -> Unit,
+ onChangeWeightLbsFieldValue: (String) -> Unit,
+ onDelete: () -> Unit,
+ onSave: () -> Unit
+) {
+
+ val keyboard = LocalSoftwareKeyboardController.current
+
+ BottomSheetSurface {
+
+ Column(
+ Modifier
+ .fillMaxWidth()
+ .navigationBarsPadding()
+ ) {
+
+ TopBar(
+ title = stringResource(R.string.barbell),
+ statusBarEnabled = false,
+ elevationEnabled = false
+ )
+
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(
+ start = 16.dp,
+ end = 16.dp,
+ bottom = 16.dp,
+ top = 8.dp
+ ),
+ verticalArrangement = Arrangement.spacedBy(8.dp),
+ ) {
+
+ AppTextField(
+ modifier = Modifier,
+ value = nameFieldValue,
+ labelValue = stringResource(R.string.name),
+ singleLine = true,
+ keyboardOptions = KeyboardOptions(
+ capitalization = KeyboardCapitalization.Sentences,
+ keyboardType = KeyboardType.Text,
+ imeAction = ImeAction.Next,
+ ),
+ onValueChange = onChangeNameFieldValue
+ )
+ AppTextField(
+ modifier = Modifier,
+ value = weightKgFieldValue,
+ labelValue = stringResource(R.string.weight_kg),
+ singleLine = true,
+ keyboardOptions = KeyboardOptions(
+ keyboardType = KeyboardType.Decimal,
+ imeAction = ImeAction.Next,
+ ),
+ onValueChange = onChangeWeightKgFieldValue
+ )
+ AppTextField(
+ modifier = Modifier,
+ value = weightLbsFieldValue,
+ labelValue = stringResource(R.string.weight_lbs),
+ singleLine = true,
+ keyboardOptions = KeyboardOptions(
+ keyboardType = KeyboardType.Decimal,
+ imeAction = ImeAction.Done,
+ ),
+ keyboardActions = KeyboardActions(
+ onDone = {
+ if (isSaveBtnEnabled) {
+ onSave()
+ } else {
+ keyboard?.hide()
+ }
+ }
+ ),
+ onValueChange = onChangeWeightLbsFieldValue
+ )
+ }
+
+ Row(
+ Modifier
+ .padding(end = 16.dp, start = 16.dp, bottom = 16.dp)
+ .align(Alignment.End)
+ ) {
+ BottomSheetSecondaryRButton(
+ modifier = Modifier.padding(end = 16.dp),
+ onClick = {
+ onDelete()
+ }) {
+ Text(if (isUpdate) stringResource(R.string.delete) else stringResource(R.string.cancel))
+ }
+
+
+ BottomSheetRButton(
+ enabled = isSaveBtnEnabled,
+ onClick = {
+ onSave()
+ },
+ modifier = Modifier.width(88.dp)
+ ) {
+ Text(if (isUpdate) stringResource(R.string.save) else stringResource(R.string.add))
+ }
+
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/ui-customize-barbells/src/main/java/com/ankitsuda/rebound/ui/customizebarbells/edit/BarbellEditBottomSheetViewModel.kt b/modules/ui-customize-barbells/src/main/java/com/ankitsuda/rebound/ui/customizebarbells/edit/BarbellEditBottomSheetViewModel.kt
new file mode 100644
index 00000000..6e307000
--- /dev/null
+++ b/modules/ui-customize-barbells/src/main/java/com/ankitsuda/rebound/ui/customizebarbells/edit/BarbellEditBottomSheetViewModel.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2022 Ankit Suda.
+ *
+ * Licensed under the GNU General Public License v3
+ *
+ * This is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ */
+
+package com.ankitsuda.rebound.ui.customizebarbells.edit
+
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.ankitsuda.base.util.toReadableString
+import com.ankitsuda.base.utils.generateId
+import com.ankitsuda.navigation.BARBELL_ID_KEY
+import com.ankitsuda.rebound.data.repositories.BarbellsRepository
+import com.ankitsuda.rebound.domain.entities.Barbell
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
+import java.time.LocalDateTime
+import javax.inject.Inject
+
+@HiltViewModel
+class BarbellEditBottomSheetViewModel @Inject constructor(
+ handle: SavedStateHandle,
+ private val barbellsRepository: BarbellsRepository
+) : ViewModel() {
+ private val barbellId = handle.get(BARBELL_ID_KEY)
+
+ private var _barbell = MutableStateFlow(null)
+ val barbell = _barbell.asStateFlow()
+
+ private var _nameFieldValue = MutableStateFlow("")
+ val nameFieldValue = _nameFieldValue.asStateFlow()
+
+ private var _weightKgFieldValue = MutableStateFlow("")
+ val weightKgFieldValue = _weightKgFieldValue.asStateFlow()
+
+ private var _weightLbsFieldValue = MutableStateFlow("")
+ val weightLbsFieldValue = _weightLbsFieldValue.asStateFlow()
+
+ val isUpdate = barbellId != null
+
+ init {
+ if (barbellId != null) {
+ viewModelScope.launch {
+ val mBarbell = barbellsRepository.getBarbell(barbellId).first()
+
+ _nameFieldValue.value = mBarbell.name ?: ""
+ _weightKgFieldValue.value = mBarbell.weightKg?.toReadableString() ?: ""
+ _weightLbsFieldValue.value = mBarbell.weightLbs?.toReadableString() ?: ""
+
+ _barbell.value = mBarbell
+ }
+ }
+ }
+
+ fun updateNameFieldValue(value: String) {
+ _nameFieldValue.value = value
+ }
+
+ fun updateWeightKgFieldValue(value: String) {
+ if (value.isEmpty() || value.toDoubleOrNull() != null) {
+ _weightKgFieldValue.value = value
+ }
+ }
+
+ fun updateWeightLbsFieldValue(value: String) {
+ if (value.isEmpty() || value.toDoubleOrNull() != null) {
+ _weightLbsFieldValue.value = value
+ }
+ }
+
+ fun deleteBarbell(onDeleted: () -> Unit) {
+ viewModelScope.launch {
+ _barbell.value?.id?.let {
+ barbellsRepository.deleteBarbell(it)
+ }
+ onDeleted()
+ }
+ }
+
+ fun saveBarbell(onSaved: () -> Unit) {
+ viewModelScope.launch {
+ val time = LocalDateTime.now()
+ val lastBarbell = (_barbell.value ?: Barbell(
+ id = generateId(),
+ isActive = true,
+ createdAt = time,
+ ))
+
+ val mBarbell = lastBarbell.copy(
+ name = _nameFieldValue.value.takeIf { it.isNotBlank() } ?: lastBarbell.name,
+ weightKg = _weightKgFieldValue.value.toDoubleOrNull() ?: lastBarbell.weightKg,
+ weightLbs = _weightLbsFieldValue.value.toDoubleOrNull() ?: lastBarbell.weightLbs,
+ updatedAt = time
+ )
+
+ barbellsRepository.upsertBarbell(mBarbell)
+
+ onSaved()
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/ui-settings/src/main/java/com/ankitsuda/rebound/ui/settings/SettingsScreen.kt b/modules/ui-settings/src/main/java/com/ankitsuda/rebound/ui/settings/SettingsScreen.kt
index 1a711513..14f761c1 100644
--- a/modules/ui-settings/src/main/java/com/ankitsuda/rebound/ui/settings/SettingsScreen.kt
+++ b/modules/ui-settings/src/main/java/com/ankitsuda/rebound/ui/settings/SettingsScreen.kt
@@ -38,6 +38,7 @@ import com.ankitsuda.rebound.ui.components.MoreSectionHeader
import com.ankitsuda.rebound.ui.components.TopBar2
import com.ankitsuda.rebound.ui.components.TopBarBackIconButton
import com.ankitsuda.rebound.ui.components.settings.PopupItemsSettingsItem
+import com.ankitsuda.rebound.ui.icons.Barbell
import com.ankitsuda.rebound.ui.icons.Plates
import me.onebone.toolbar.CollapsingToolbarScaffold
import me.onebone.toolbar.ScrollStrategy
@@ -102,6 +103,17 @@ fun SettingsScreen(
navigator.navigate(LeafScreen.Plates().createRoute())
})
}
+ item {
+ MoreItemCard(
+ modifier = Modifier
+ .fillMaxWidth(),
+ icon = Icons.Filled.Barbell,
+ text = stringResource(R.string.barbells),
+ description = stringResource(R.string.customize_barbells),
+ onClick = {
+ navigator.navigate(LeafScreen.Barbells().createRoute())
+ })
+ }
item {
MoreSectionHeader(text = stringResource(R.string.defaults))
}
diff --git a/settings.gradle b/settings.gradle
index b560cd9e..ecd920b1 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -54,3 +54,4 @@ include ':modules:ui-workout-edit'
include ':modules:ui-keyboard'
include ':modules:ui-customize-plates'
include ':modules:common-ui-components-number-picker'
+include ':modules:ui-customize-barbells'
diff --git a/svgs/barbell.svg b/svgs/barbell.svg
new file mode 100644
index 00000000..3820bdcc
--- /dev/null
+++ b/svgs/barbell.svg
@@ -0,0 +1,5 @@
+