Skip to content
Open
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
760813f
add: import & export buttons in SettingsScreen
mnjxclone Apr 25, 2026
c17da6d
update: add import and export functionality
mnjxclone Apr 25, 2026
daed80b
progress on a migration from raw SQL backup/restore to a JSON arch.
mnjxclone Apr 29, 2026
1d61653
update: JSON data export/import
mnjxclone May 3, 2026
e8bb260
fix: remove temporary logging
mnjxclone May 3, 2026
8e2aed6
fix: remove to-be-deprecated content
mnjxclone May 9, 2026
3bc0c36
fix: proper use of ioDispatcher
mnjxclone May 9, 2026
a6b6f97
fix: remove unused imports
mnjxclone May 9, 2026
eb46859
fix: remove redundant lib in libs.versions.toml
mnjxclone May 9, 2026
f1ee9b5
fix: repair build.gradle.kts
mnjxclone May 9, 2026
70c7dc0
fix: use standard naming conventions
mnjxclone May 9, 2026
cab0cc9
fix: use correct serializer for LocalDateTime
mnjxclone May 9, 2026
aab175e
fix: remove unused variables
mnjxclone May 9, 2026
703cfb7
fix: revert prev commit
mnjxclone May 9, 2026
3d0f7aa
fix: remove unnecessary upsert methods
mnjxclone May 9, 2026
3ecf5e0
fix: correct export/import pipeline (but allows duplicates)
mnjxclone May 9, 2026
e7f57bc
fix: convert toasts to snackbars
mnjxclone May 9, 2026
0d1f0c6
resolve merge conflicts with upstream
mnjxclone May 25, 2026
7d6d1cf
add: missing generic savePreferences function
mnjxclone May 25, 2026
3988156
add: datastore dependency
mnjxclone May 25, 2026
2240b70
fix: correct jvm version
mnjxclone May 25, 2026
67efb57
update: use dialogs instead of snackbars
mnjxclone May 25, 2026
d7000c9
fix: fix linted errors
mnjxclone May 25, 2026
29d6a42
fix: add proper migration logic
mnjxclone May 25, 2026
01a3c42
fix: fix typo and add animations to loading
mnjxclone May 25, 2026
edb8a62
fix: separated concerns regarding SettingsEvents
mnjxclone May 25, 2026
48d9855
fix: remove redundant datastore dependency
mnjxclone Jun 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ dependencyLocking {
dependencies {

implementation(libs.androidx.core.ktx)
implementation(libs.androidx.datastore.preferences.core)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
Expand Down
8 changes: 6 additions & 2 deletions app/src/main/java/org/librefit/db/AppDatabase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ import org.librefit.db.entity.Measurement
import org.librefit.db.entity.Set
import org.librefit.db.entity.Workout

object Schema {
const val VERSION = 3
}

@Database(
entities = [Workout::class, Exercise::class, Set::class, Measurement::class, ExerciseDC::class],
version = 3,
version = Schema.VERSION,
exportSchema = true,
autoMigrations = [
AutoMigration(from = 1, to = 2)
Expand Down Expand Up @@ -78,4 +82,4 @@ abstract class AppDatabase : RoomDatabase() {
abstract fun getMeasurementDao(): MeasurementDao

abstract fun getDatasetDao(): DatasetDao
}
}
3 changes: 3 additions & 0 deletions app/src/main/java/org/librefit/db/dao/DatasetDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ interface DatasetDao {
@Query("SELECT * FROM dataset ORDER BY name")
fun getDataset(): Flow<List<ExerciseDC>>

@Query("SELECT * FROM dataset ORDER BY name")
suspend fun getDatasetOnce(): List<ExerciseDC>

@Query("SELECT * FROM dataset WHERE isCustomExercise")
fun getCustomExercises(): Flow<List<ExerciseDC>>

Expand Down
3 changes: 3 additions & 0 deletions app/src/main/java/org/librefit/db/dao/MeasurementDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ interface MeasurementDao {
@Query("SELECT * FROM measurements ORDER BY date DESC")
fun getAllMeasurements(): Flow<List<Measurement>>

@Query("SELECT * FROM measurements ORDER BY date DESC")
suspend fun getAllMeasurementsOnce(): List<Measurement>

@Query("SELECT * FROM measurements WHERE date <= :cutoff ORDER BY date DESC")
suspend fun getLastMeasurementByCutoff(cutoff: LocalDateTime): Measurement?
}
26 changes: 26 additions & 0 deletions app/src/main/java/org/librefit/db/dao/WorkoutDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ import androidx.room.Insert
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Update
import androidx.room.Upsert
import kotlinx.coroutines.flow.Flow
import org.librefit.db.entity.Exercise
import org.librefit.db.entity.Measurement
import org.librefit.db.entity.Set
import org.librefit.db.entity.Workout
import org.librefit.db.relations.ExerciseWithSets
Expand All @@ -25,6 +27,30 @@ import java.time.LocalDateTime

@Dao
interface WorkoutDao {
/**
* Returns a list of [org.librefit.db.entity.Workout]s ordered by their creation date
*/
@Query("SELECT * FROM workouts ORDER BY created")
suspend fun getAllWorkouts(): List<Workout>

@Query("SELECT * FROM workouts ORDER BY created")
fun getAllWorkoutsWithExercisesAndSets(): Flow<List<WorkoutWithExercisesAndSets>>

@Query("SELECT * FROM workouts ORDER BY created")
suspend fun getAllWorkoutsWithExercisesAndSetsOnce(): List<WorkoutWithExercisesAndSets>

/**
* Returns a list of [org.librefit.db.entity.Exercise]s for each workout
*/
@Query("SELECT * FROM exercises WHERE workoutId IN (:workoutIds)")
suspend fun getAllExercises(workoutIds: List<Long>): List<Exercise>

/**
* Returns a list of [org.librefit.db.entity.Set]s for each exercise
*/
@Query("SELECT * FROM sets WHERE exerciseId IN (:exerciseIds)")
suspend fun getAllSets(exerciseIds: List<Long>): List<Set>

/**
* Returns a flow that emits a stream of [org.librefit.db.entity.Workout]s filtered by [state]
*/
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/java/org/librefit/db/entity/Measurement.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@ import androidx.annotation.FloatRange
import androidx.annotation.IntRange
import androidx.room.Entity
import androidx.room.PrimaryKey
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import java.time.LocalDateTime

@Entity(tableName = "measurements")
@Serializable
data class Measurement(
@PrimaryKey(autoGenerate = true) val id: Long = 0L,
@get:FloatRange(0.0, 300.0) val bodyWeight: Double = 0.0,
@get:IntRange(0, 100) val bodyFatPercentage: Int = 0,
@get:IntRange(0, 100) val muscleMassPercentage: Int = 0,
@Serializable(with = LocalDateTimeSerializer::class)
val date: LocalDateTime = LocalDateTime.now(),
val notes: String = ""
)
10 changes: 10 additions & 0 deletions app/src/main/java/org/librefit/db/importExport/dto/ExportData.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.librefit.db.importExport.dto

import kotlinx.serialization.Serializable
import org.librefit.db.entity.Measurement

@Serializable
data class ExportData(
val workouts: List<ExportWorkout>,
val measurements: List<Measurement>,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.librefit.db.importExport.dto

import kotlinx.serialization.Serializable
import org.librefit.enums.SetMode

@Serializable
data class ExportExercise(
val id: Long,
val idExerciseDC: String,
val notes: String,
val setMode: SetMode,
val restTime: Int,
val position: Int,
val workoutId: Long,
val sets: List<ExportSet>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.librefit.db.importExport.dto

import kotlinx.serialization.Serializable

@Serializable
data class ExportPayload(
val schemaVersion: Int,
val data: ExportData
)
13 changes: 13 additions & 0 deletions app/src/main/java/org/librefit/db/importExport/dto/ExportSet.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.librefit.db.importExport.dto

import kotlinx.serialization.Serializable

@Serializable
data class ExportSet(
val id: Long,
val load: Double,
val reps: Int,
val elapsedTime: Int,
val completed: Boolean,
val exerciseId: Long
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.librefit.db.importExport.dto

import kotlinx.serialization.Serializable
import org.librefit.db.entity.LocalDateTimeSerializer
import org.librefit.enums.WorkoutState
import java.time.LocalDateTime

@Serializable
data class ExportWorkout(
val id: Long,
val routineId: Long,
val notes: String,
val title: String,
val state: WorkoutState,
val timeElapsed: Int,
@Serializable(with = LocalDateTimeSerializer::class)
val created: LocalDateTime,
@Serializable(with = LocalDateTimeSerializer::class)
val completed: LocalDateTime,
val exercises: List<ExportExercise>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.librefit.db.importExport.mapper

import org.librefit.db.importExport.dto.ExportExercise
import org.librefit.db.importExport.dto.ExportSet
import org.librefit.db.importExport.dto.ExportWorkout
import org.librefit.db.relations.WorkoutWithExercisesAndSets

fun WorkoutWithExercisesAndSets.toExport(): ExportWorkout {
return ExportWorkout(
id = workout.id,
routineId = workout.routineId,
notes = workout.notes,
title = workout.title,
state = workout.state,
timeElapsed = workout.timeElapsed,
created = workout.created,
completed = workout.completed,
exercises = exercisesWithSets.map { ex ->
ExportExercise(
id = ex.exercise.id,
idExerciseDC = ex.exercise.idExerciseDC,
notes = ex.exercise.notes,
setMode = ex.exercise.setMode,
restTime = ex.exercise.restTime,
position = ex.exercise.position,
workoutId = ex.exercise.workoutId,
sets = ex.sets.map { s ->
ExportSet(
id = s.id,
load = s.load,
reps = s.reps,
elapsedTime = s.elapsedTime,
completed = s.completed,
exerciseId = s.exerciseId
)
}
)
}
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.librefit.db.importExport.mapper

import org.librefit.db.entity.Exercise
import org.librefit.db.entity.Workout
import org.librefit.db.entity.Set
import org.librefit.db.importExport.dto.ExportWorkout
import org.librefit.db.relations.ExerciseWithSets
import org.librefit.db.relations.WorkoutWithExercisesAndSets

fun ExportWorkout.toRelation(): WorkoutWithExercisesAndSets {
val workoutEntity = Workout(
id = 0, // 0 -> then new unique ID assigned by ROOM
routineId = routineId,
notes = notes,
title = title,
state = state,
timeElapsed = timeElapsed,
created = created,
completed = completed
)

val exerciseRelations = exercises.map { ex ->
val exerciseEntity = Exercise(
id = 0, // 0 -> then new unique ID assigned by ROOM
idExerciseDC = ex.idExerciseDC,
notes = ex.notes,
setMode = ex.setMode,
restTime = ex.restTime,
position = ex.position,
workoutId = id
)

val sets = ex.sets.map { s ->
Set(
id = 0, // 0 -> then new unique ID assigned by ROOM
load = s.load,
reps = s.reps,
elapsedTime = s.elapsedTime,
completed = s.completed,
exerciseId = ex.id
)
}

ExerciseWithSets(
exercise = exerciseEntity,
sets = sets
)
}

return WorkoutWithExercisesAndSets(
workout = workoutEntity,
exercisesWithSets = exerciseRelations
)
}
Loading