diff --git a/app/schemas/com.example.timert.com.example.timert.data.database.AppDatabase/1.json b/app/schemas/com.example.timert.com.example.timert.data.database.AppDatabase/1.json new file mode 100644 index 0000000..bb0bd47 --- /dev/null +++ b/app/schemas/com.example.timert.com.example.timert.data.database.AppDatabase/1.json @@ -0,0 +1,67 @@ +{ + "formatVersion": 1, + "database": { + "version": 1, + "identityHash": "6ba5dfef7859cedba19af71b404c9e4f", + "entities": [ + { + "tableName": "workouts", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `numApproaches` INTEGER NOT NULL, `numExercises` INTEGER NOT NULL, `restBetweenExercises` INTEGER NOT NULL, `restBetweenApproaches` INTEGER NOT NULL, `exerciseDuration` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "numApproaches", + "columnName": "numApproaches", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "numExercises", + "columnName": "numExercises", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "restBetweenExercises", + "columnName": "restBetweenExercises", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "restBetweenApproaches", + "columnName": "restBetweenApproaches", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exerciseDuration", + "columnName": "exerciseDuration", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + } + } + ], + "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, '6ba5dfef7859cedba19af71b404c9e4f')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/AppDatabase.kt b/app/src/main/java/AppDatabase.kt deleted file mode 100644 index 9269316..0000000 --- a/app/src/main/java/AppDatabase.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.example.timert -import androidx.room.Database -import androidx.room.RoomDatabase -import com.example.timert.Workout -import com.example.timert.WorkoutDao -@Database(entities = [Workout::class], version = 1) -abstract class AppDatabase : RoomDatabase() { - abstract fun workoutDao(): WorkoutDao -} \ No newline at end of file diff --git a/app/src/main/java/WorkoutAdapter.kt b/app/src/main/java/WorkoutAdapter.kt deleted file mode 100644 index 63a62da..0000000 --- a/app/src/main/java/WorkoutAdapter.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.example.timert - -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.TextView -import androidx.recyclerview.widget.RecyclerView - -class WorkoutAdapter(private val workouts: List) : RecyclerView.Adapter() { - - class WorkoutViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - val textViewName: TextView = itemView.findViewById(R.id.textViewName) - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WorkoutViewHolder { - val itemView = LayoutInflater.from(parent.context) - .inflate(R.layout.workout_item, parent, false) - return WorkoutViewHolder(itemView) - } - - override fun onBindViewHolder(holder: WorkoutViewHolder, position: Int) { - val currentItem = workouts[position] - holder.textViewName.text = currentItem.name - } - - override fun getItemCount() = workouts.size -} \ No newline at end of file diff --git a/app/src/main/java/com/example/timert/AddWorkout.kt b/app/src/main/java/com/example/timert/AddWorkout.kt index e4d762a..c23779e 100644 --- a/app/src/main/java/com/example/timert/AddWorkout.kt +++ b/app/src/main/java/com/example/timert/AddWorkout.kt @@ -6,13 +6,24 @@ import android.os.Bundle import android.widget.Button import android.widget.EditText import android.widget.ImageView +import android.widget.Toast import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat +import androidx.lifecycle.ViewModelProvider +import com.example.timert.com.example.timert.data.entity.Workout +import com.example.timert.data.viewModel.WorkoutViewModel + class AddWorkout : AppCompatActivity() { private var isSave = false + private lateinit var editWorkoutView: EditText + private lateinit var numWorkoutView: EditText + private lateinit var numRestWorkoutView: EditText + private lateinit var numRestSetsView: EditText + private lateinit var numSetsView: EditText + private lateinit var timeWorkoutView: EditText override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -23,11 +34,62 @@ class AddWorkout : AppCompatActivity() { v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) insets } + + + val workoutViewModel = ViewModelProvider( + this, + ViewModelProvider.AndroidViewModelFactory.getInstance(application) + )[WorkoutViewModel::class.java] + + editWorkoutView = findViewById(R.id.name_training) + numWorkoutView = findViewById(R.id.num_exercise) + numRestWorkoutView = findViewById(R.id.rest_workout_all) + numRestSetsView = findViewById(R.id.rest_sets_all) + numSetsView = findViewById(R.id.numbers_of_sets_all) + timeWorkoutView = findViewById(R.id.time_work_all) + + val buttonSaveAll: Button = findViewById(R.id.btn_save_all) buttonSaveAll.setOnClickListener { - val intent = Intent(this, MainActivity::class.java) - startActivity(intent) - isSave = true + + val workoutName = editWorkoutView.text.toString() + val numWorkoutText = numWorkoutView.text.toString() + val restWorkoutText = numRestWorkoutView.text.toString() + val restSetsText = numRestSetsView.text.toString() + val numSetsText = numSetsView.text.toString() + val timeWorkText = timeWorkoutView.text.toString() + + + + if (workoutName.isBlank() || numSetsText.isBlank() || restSetsText.isBlank()||numWorkoutText.isBlank() || restWorkoutText.isBlank() || timeWorkText.isBlank()) { + Toast.makeText(this, "Заполните все поля", Toast.LENGTH_SHORT).show() + } else { + val numExercise = numWorkoutText.toIntOrNull() + val restExercise = restWorkoutText.toIntOrNull() + val numSets = numSetsText.toIntOrNull() + val restSets = restSetsText.toIntOrNull() + val timeWorkExercise = timeWorkText.toIntOrNull() + + if (numSets == null || restSets == null || numExercise == null || restExercise == null || timeWorkExercise == null) { + Toast.makeText(this, "Неверный формат чисел", Toast.LENGTH_SHORT).show() + return@setOnClickListener + } + + val newWorkout = Workout( + name = workoutName, + numApproaches = numSets, + numExercises = numExercise, + restBetweenExercises = restExercise, + restBetweenApproaches = restSets, + exerciseDuration = timeWorkExercise, + ) + workoutViewModel.insert(newWorkout) + + Toast.makeText(this, "Упражнение сохранено", Toast.LENGTH_SHORT).show() + + isSave = true + + } } val button_on_main_all: Button = findViewById(R.id.btn_on_main_all) @@ -71,10 +133,16 @@ class AddWorkout : AppCompatActivity() { val Plus: ImageView = findViewById(R.id.add_set) val Minus: ImageView = findViewById(R.id.del_set) + val numExer: EditText = findViewById(R.id.num_exercise) + val PlusExer: ImageView = findViewById(R.id.add_set4) + val MinusExer: ImageView = findViewById(R.id.del_set4) + + setupPlusMinus(timeInputRestSetAll, btnPlusTimeAll, btnMinusTimeAll, minValue = 5, maxValue = 600) setupPlusMinus(setsInputAll, btnPlusSetsAll, btnMinusSetsAll, minValue = 1, maxValue = 20) setupPlusMinus(timeInputRest, btnPlusTimeWor, btnMinusTimeWor, minValue = 5, maxValue = 600) setupPlusMinus(timeWork, Plus, Minus, minValue = 5, maxValue = 600) + setupPlusMinus(numExer, PlusExer, MinusExer, minValue = 1, maxValue = 50) } diff --git a/app/src/main/java/com/example/timert/AllTraining.kt b/app/src/main/java/com/example/timert/AllTraining.kt index d18283b..b69d582 100644 --- a/app/src/main/java/com/example/timert/AllTraining.kt +++ b/app/src/main/java/com/example/timert/AllTraining.kt @@ -59,7 +59,8 @@ class AllTraining : AppCompatActivity() { val newExercise = Exercise( name_exercise = exerciseName, num_sets = numSets, - rest_of_sets = restSets + rest_of_sets = restSets, + trainingId = 0 ) exerciseViewModel.insert(newExercise) Toast.makeText(this, "Упражнение сохранено", Toast.LENGTH_SHORT).show() diff --git a/app/src/main/java/com/example/timert/CreatingTraining.kt b/app/src/main/java/com/example/timert/CreatingTraining.kt index cafa793..512133e 100644 --- a/app/src/main/java/com/example/timert/CreatingTraining.kt +++ b/app/src/main/java/com/example/timert/CreatingTraining.kt @@ -7,6 +7,7 @@ import android.widget.Button import android.widget.EditText import android.widget.ImageButton import android.widget.ImageView +import android.widget.Toast import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat @@ -15,18 +16,30 @@ import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.example.timert.data.adapter.ExerciseAdapter +import com.example.timert.data.entity.Training import com.example.timert.data.viewModel.ExerciseViewModel +import com.example.timert.data.viewModel.TrainingViewModel +import com.example.timert.data.viewModel.WorkoutViewModel class CreatingTraining : AppCompatActivity() { private lateinit var viewModel: ExerciseViewModel + private lateinit var name_training: EditText + private lateinit var numSets: EditText + private lateinit var restSets: EditText + private var isSave = false + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContentView(R.layout.activity_creating_training) - + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> + val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) + insets + } @@ -36,6 +49,10 @@ class CreatingTraining : AppCompatActivity() { )[ExerciseViewModel::class.java] + name_training = findViewById(R.id.name_training) + numSets = findViewById(R.id.numbers_of_sets) + restSets = findViewById(R.id.rest_sets) + val adapter = ExerciseAdapter { id -> viewModel.deleteExerciseById(id) } @@ -50,6 +67,13 @@ class CreatingTraining : AppCompatActivity() { adapter.setExercises(it) } } + + val trainingViewModel = ViewModelProvider( + this, + ViewModelProvider.AndroidViewModelFactory.getInstance(application) + )[TrainingViewModel::class.java] + + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) @@ -57,22 +81,46 @@ class CreatingTraining : AppCompatActivity() { } val buttonSave: Button = findViewById(R.id.btn_save) buttonSave.setOnClickListener { + val name = name_training.text.toString() + val numS = numSets.text.toString() + val resS = restSets.text.toString() - isSave=true - val intent = Intent(this, MainActivity::class.java) - startActivity(intent) + if (name.isBlank() || numS.isBlank() || resS.isBlank()) { + Toast.makeText(this, "Заполните все поля", Toast.LENGTH_SHORT).show() + } else { + + val sets = numS.toIntOrNull() + val rest = resS.toIntOrNull() + + if (sets == null || rest == null) { + Toast.makeText(this, "Неверный формат чисел", Toast.LENGTH_SHORT).show() + return@setOnClickListener + } + + val newTraining = Training( + name = name, + numSets = sets, + restBetweenSets = rest) + + trainingViewModel.insertTrainingAndLinkExercises(newTraining) + + Toast.makeText(this, "Тренировка сохранена", Toast.LENGTH_SHORT).show() + + + isSave = true + } } - val timeInputRest: EditText = findViewById(R.id.rest_sets) + val btnPlusTime: ImageView = findViewById(R.id.add_res) val btnMinusTime: ImageView = findViewById(R.id.del_res) - val setsInput: EditText = findViewById(R.id.numbers_of_sets) + val btnPlusSets: ImageView = findViewById(R.id.add_set) val btnMinusSets: ImageView = findViewById(R.id.del_set) - setupPlusMinus(timeInputRest, btnPlusTime, btnMinusTime, minValue = 5, maxValue = 600) - setupPlusMinus(setsInput, btnPlusSets, btnMinusSets, minValue = 1, maxValue = 20) + setupPlusMinus(restSets, btnPlusTime, btnMinusTime, minValue = 5, maxValue = 600) + setupPlusMinus(numSets, btnPlusSets, btnMinusSets, minValue = 1, maxValue = 20) val button_on_main: Button = findViewById(R.id.btn_on_main) button_on_main.setOnClickListener { if(isSave==false){ diff --git a/app/src/main/java/com/example/timert/History.kt b/app/src/main/java/com/example/timert/History.kt index 6642667..b538a28 100644 --- a/app/src/main/java/com/example/timert/History.kt +++ b/app/src/main/java/com/example/timert/History.kt @@ -26,5 +26,11 @@ class History : AppCompatActivity() { recyclerViewHistory.layoutManager = LinearLayoutManager(this) + + val button_on_main: Button = findViewById(R.id.btn_on_main) + button_on_main.setOnClickListener { + val intent = Intent(this, MainActivity::class.java) + startActivity(intent) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/timert/MainActivity.kt b/app/src/main/java/com/example/timert/MainActivity.kt index b607269..8c4dc3c 100644 --- a/app/src/main/java/com/example/timert/MainActivity.kt +++ b/app/src/main/java/com/example/timert/MainActivity.kt @@ -2,18 +2,26 @@ package com.example.timert import android.content.Intent import android.os.Bundle -import android.util.Log import android.widget.Button import android.widget.Toast import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.example.timert.com.example.timert.data.adapter.WorkoutAdapter +import com.example.timert.data.TrainingItem +import com.example.timert.data.adapter.TrainingAdapter +import com.example.timert.data.viewModel.TrainingViewModel +import com.example.timert.data.viewModel.WorkoutViewModel class MainActivity : AppCompatActivity() { - - + private lateinit var adapter: TrainingAdapter + private lateinit var workoutViewModel: WorkoutViewModel + private lateinit var trainingViewModel: TrainingViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -24,6 +32,44 @@ class MainActivity : AppCompatActivity() { v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) insets } + adapter = TrainingAdapter( + onDeleteWorkout = { id -> workoutViewModel.deleteWorkoutById(id) }, + onDeleteTraining = { id -> trainingViewModel.deleteTrainingById(id) } + ) + + val recyclerView = findViewById(R.id.workout) + recyclerView.layoutManager = LinearLayoutManager(this) + recyclerView.adapter = adapter + + workoutViewModel = ViewModelProvider(this)[WorkoutViewModel::class.java] + trainingViewModel = ViewModelProvider(this)[TrainingViewModel::class.java] + + // объединяем данные вручную + val combinedList = mutableListOf() + + workoutViewModel.allWorkouts.observe(this) { workouts -> + combinedList.clear() + combinedList.addAll(workouts.map { TrainingItem.WorkoutItem(it) }) + + trainingViewModel.allTrainingsWithExercises.value?.let { trainings -> + combinedList.addAll(trainings.map { TrainingItem.TrainingWithExercisesItem(it) }) + } + + adapter.submitList(combinedList.toList()) + } + + trainingViewModel.allTrainingsWithExercises.observe(this) { trainings -> + combinedList.clear() + workoutViewModel.allWorkouts.value?.let { workouts -> + combinedList.addAll(workouts.map { TrainingItem.WorkoutItem(it) }) + } + combinedList.addAll(trainings.map {TrainingItem.TrainingWithExercisesItem(it) }) + adapter.submitList(combinedList.toList()) + } + + + + // Кнопки главной формы: val settingsButton: Button = findViewById(R.id.settingsButton) diff --git a/app/src/main/java/com/example/timert/MyApplication.kt b/app/src/main/java/com/example/timert/MyApplication.kt index 1606441..2d15bd9 100644 --- a/app/src/main/java/com/example/timert/MyApplication.kt +++ b/app/src/main/java/com/example/timert/MyApplication.kt @@ -2,7 +2,7 @@ package com.example.timert import android.app.Application import androidx.room.Room -import com.example.timert.AppDatabase +import com.example.timert.com.example.timert.data.database.AppDatabase class MyApplication : Application() { companion object { diff --git a/app/src/main/java/com/example/timert/data/TrainingItem.kt b/app/src/main/java/com/example/timert/data/TrainingItem.kt new file mode 100644 index 0000000..a516591 --- /dev/null +++ b/app/src/main/java/com/example/timert/data/TrainingItem.kt @@ -0,0 +1,9 @@ +package com.example.timert.data + +import com.example.timert.com.example.timert.data.entity.Workout +import com.example.timert.data.entity.Training + +sealed class TrainingItem { + data class WorkoutItem(val workout: Workout) : TrainingItem() + data class TrainingWithExercisesItem(val trainingWithExercise: trainingWithExercise) : TrainingItem() +} \ No newline at end of file diff --git a/app/src/main/java/com/example/timert/data/adapter/ExerciseAdapter.kt b/app/src/main/java/com/example/timert/data/adapter/ExerciseAdapter.kt index d19ea94..68791b0 100644 --- a/app/src/main/java/com/example/timert/data/adapter/ExerciseAdapter.kt +++ b/app/src/main/java/com/example/timert/data/adapter/ExerciseAdapter.kt @@ -1,18 +1,13 @@ package com.example.timert.data.adapter -import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Button import android.widget.ImageButton import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import com.example.timert.R import com.example.timert.data.entity.Exercise -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch //class ExerciseAdapter internal constructor(context: Context):RecyclerView.Adapter(){ diff --git a/app/src/main/java/com/example/timert/data/adapter/TrainingAdapter.kt b/app/src/main/java/com/example/timert/data/adapter/TrainingAdapter.kt new file mode 100644 index 0000000..836f1a2 --- /dev/null +++ b/app/src/main/java/com/example/timert/data/adapter/TrainingAdapter.kt @@ -0,0 +1,80 @@ +package com.example.timert.data.adapter + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import android.widget.ImageButton +import android.widget.TextView +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import com.example.timert.R +import com.example.timert.com.example.timert.data.entity.Workout +import com.example.timert.data.TrainingItem +import com.example.timert.data.entity.Training +import com.example.timert.data.trainingWithExercise + +class TrainingAdapter( private val onDeleteWorkout: (Int) -> Unit, + private val onDeleteTraining: (Int) -> Unit +) : ListAdapter(DiffCallback()) { + + companion object { + private const val TYPE_WORKOUT = 0 + private const val TYPE_TRAINING_WITH_EXERCISES = 1 + } + + override fun getItemViewType(position: Int): Int { + return when (getItem(position)) { + is TrainingItem.WorkoutItem -> TYPE_WORKOUT + is TrainingItem.TrainingWithExercisesItem -> TYPE_TRAINING_WITH_EXERCISES + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + val view = inflater.inflate(R.layout.training_item_layout, parent, false) // один layout + return when (viewType) { + TYPE_WORKOUT -> WorkoutViewHolder(view, onDeleteWorkout) + TYPE_TRAINING_WITH_EXERCISES -> TrainingViewHolder(view, onDeleteTraining) + else -> throw IllegalArgumentException("Invalid view type") + } + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (val item = getItem(position)) { + is TrainingItem.WorkoutItem -> (holder as WorkoutViewHolder).bind(item.workout) + is TrainingItem.TrainingWithExercisesItem -> (holder as TrainingViewHolder).bind(item.trainingWithExercise) + } + } + + class WorkoutViewHolder(itemView: View, private val onDelete: (Int) -> Unit) : + RecyclerView.ViewHolder(itemView) { + fun bind(workout: Workout) { + itemView.findViewById(R.id.training_text).text = workout.name + itemView.findViewById(R.id.deleteButtonTraining).setOnClickListener { + onDelete(workout.id) + } + } + } + + class TrainingViewHolder(itemView: View, private val onDelete: (Int) -> Unit) : + RecyclerView.ViewHolder(itemView) { + fun bind(trainingWithExercise: trainingWithExercise) { + itemView.findViewById(R.id.training_text).text = trainingWithExercise.training.name + itemView.findViewById(R.id.deleteButtonTraining).setOnClickListener { + onDelete(trainingWithExercise.training.id) + } + } + } + + class DiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: TrainingItem, newItem: TrainingItem): Boolean { + return oldItem == newItem + } + + override fun areContentsTheSame(oldItem: TrainingItem, newItem: TrainingItem): Boolean { + return oldItem == newItem + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/timert/data/adapter/WorkoutAdapter.kt b/app/src/main/java/com/example/timert/data/adapter/WorkoutAdapter.kt new file mode 100644 index 0000000..17d8f04 --- /dev/null +++ b/app/src/main/java/com/example/timert/data/adapter/WorkoutAdapter.kt @@ -0,0 +1,48 @@ +package com.example.timert.com.example.timert.data.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageButton +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.example.timert.R +import com.example.timert.com.example.timert.data.entity.Workout + + + +class WorkoutAdapter internal constructor(private val onDeleteClick: (Int) -> Unit +) : RecyclerView.Adapter() { + private var workouts: List = emptyList() + + inner class WorkoutViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val textViewName: TextView = itemView.findViewById(R.id.training_text) + val deleteButton: ImageButton = itemView.findViewById(R.id.deleteButtonTraining) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WorkoutViewHolder { + val itemView = LayoutInflater.from(parent.context) + .inflate(R.layout.training_item_layout, parent, false) + return WorkoutViewHolder(itemView) + } + + override fun onBindViewHolder(holder: WorkoutViewHolder, position: Int) { + val currentItem = workouts[position] + holder.textViewName.text = + "${currentItem.name}" + + + + holder.deleteButton.setOnClickListener { + onDeleteClick(currentItem.id) + } + } + + override fun getItemCount() = workouts.size + + fun setWorkouts(workouts: List) { + this.workouts = workouts + notifyDataSetChanged() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/timert/data/dao/ExerciseDao.kt b/app/src/main/java/com/example/timert/data/dao/ExerciseDao.kt index 95eef78..92dc283 100644 --- a/app/src/main/java/com/example/timert/data/dao/ExerciseDao.kt +++ b/app/src/main/java/com/example/timert/data/dao/ExerciseDao.kt @@ -6,11 +6,14 @@ import androidx.room.Delete import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query +import androidx.room.Transaction import com.example.timert.data.entity.Exercise @Dao interface ExerciseDao { + + @Transaction @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insert(exercise: Exercise) @@ -18,9 +21,9 @@ interface ExerciseDao { fun getExercise(): LiveData> - //@Delete - // suspend fun delete(exercise: Exercise) - @Query("DELETE FROM exercise_table WHERE id = :id") suspend fun deleteById(id: Int) + + @Query("UPDATE exercise_table SET trainingId = :newTrainingId WHERE trainingId = 0") + suspend fun updateExercisesWithTrainingId(newTrainingId: Int) } \ No newline at end of file diff --git a/app/src/main/java/com/example/timert/data/dao/TrainingDao.kt b/app/src/main/java/com/example/timert/data/dao/TrainingDao.kt new file mode 100644 index 0000000..34d1cce --- /dev/null +++ b/app/src/main/java/com/example/timert/data/dao/TrainingDao.kt @@ -0,0 +1,41 @@ +package com.example.timert.data.dao + +import androidx.lifecycle.LiveData +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Transaction +import com.example.timert.data.entity.Training +import com.example.timert.data.trainingWithExercise + + +@Dao +interface TrainingDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertTraining(training: Training) : Long + + @Query("SELECT * FROM Training WHERE id = :trainingId") + fun getTrainingWithExercise(trainingId: Int): LiveData + + @Delete + fun deleteTraining(training: Training) + + @Query("SELECT * FROM training") + fun getAllTrainings(): LiveData> + + @Query("DELETE FROM training WHERE id = :id") + suspend fun deleteById(id: Int) + + @Query("SELECT * FROM training WHERE id = :trainingId") + fun getTrainingWithExercises(trainingId: Int): LiveData + @Transaction + @Query("SELECT * FROM training") + fun getAllTrainingsWithExercises(): LiveData> + + @Transaction + @Query("SELECT * FROM training") + fun getTrainingsWithExercises(): LiveData> +} diff --git a/app/src/main/java/WorkoutDao.kt b/app/src/main/java/com/example/timert/data/dao/WorkoutDao.kt similarity index 59% rename from app/src/main/java/WorkoutDao.kt rename to app/src/main/java/com/example/timert/data/dao/WorkoutDao.kt index 26a43f6..b5d71bd 100644 --- a/app/src/main/java/WorkoutDao.kt +++ b/app/src/main/java/com/example/timert/data/dao/WorkoutDao.kt @@ -1,22 +1,29 @@ -package com.example.timert +package com.example.timert.com.example.timert.data.dao +import androidx.lifecycle.LiveData import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Update +import com.example.timert.com.example.timert.data.entity.Workout +import com.example.timert.data.entity.Exercise @Dao interface WorkoutDao { + @Insert(onConflict = OnConflictStrategy.IGNORE) suspend fun insertWorkout(workout: Workout): Long @Query("SELECT * FROM workouts") - suspend fun getAllWorkouts(): List + fun getAllWorkouts(): LiveData> @Query("SELECT * FROM workouts WHERE id = :workoutId") suspend fun getWorkoutById(workoutId: Int): Workout? @Update suspend fun updateWorkout(workout: Workout) + + @Query("DELETE FROM workouts WHERE id = :id") + suspend fun deleteById(id: Int) } \ No newline at end of file diff --git a/app/src/main/java/com/example/timert/data/database/AppDatabase.kt b/app/src/main/java/com/example/timert/data/database/AppDatabase.kt new file mode 100644 index 0000000..a421131 --- /dev/null +++ b/app/src/main/java/com/example/timert/data/database/AppDatabase.kt @@ -0,0 +1,43 @@ +package com.example.timert.com.example.timert.data.database + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import com.example.timert.com.example.timert.data.dao.WorkoutDao +import com.example.timert.com.example.timert.data.entity.Workout +import com.example.timert.data.dao.ExerciseDao +import com.example.timert.data.dao.TrainingDao +import com.example.timert.data.entity.Exercise +import com.example.timert.data.entity.Training +import kotlinx.coroutines.CoroutineScope + +@Database(entities = [Workout::class, Exercise::class, Training::class], version = 3, exportSchema = false) +abstract class AppDatabase : RoomDatabase() { + abstract fun workoutDao(): WorkoutDao + abstract fun trainingDao(): TrainingDao + abstract fun exerciseDao(): ExerciseDao + + companion object { + @Volatile + private var INSTANCE: AppDatabase? = null + fun getDatabase(context: Context): AppDatabase { + val tempInstance = INSTANCE + if (tempInstance != null) { + return tempInstance + } + synchronized(this) { + val instance = Room.databaseBuilder( + context.applicationContext, + AppDatabase::class.java, + "workout_database" + ) + //.fallbackToDestructiveMigration() + .build() + INSTANCE = instance + return instance + } + + } + } +} diff --git a/app/src/main/java/com/example/timert/data/database/ExerciseDatabase.kt b/app/src/main/java/com/example/timert/data/database/ExerciseDatabase.kt deleted file mode 100644 index 813180e..0000000 --- a/app/src/main/java/com/example/timert/data/database/ExerciseDatabase.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.example.timert.data.database - -import android.content.Context -import androidx.room.Database -import androidx.room.Room -import androidx.room.RoomDatabase -import androidx.sqlite.db.SupportSQLiteDatabase -import com.example.timert.data.dao.ExerciseDao -import com.example.timert.data.entity.Exercise -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch - -@Database(entities = [Exercise::class], version = 1, exportSchema = false) -abstract class ExerciseDatabase:RoomDatabase(){ - abstract fun exerciseDao():ExerciseDao - - // private class ExerciseDatabaseCallback (private val scope: CoroutineScope) : RoomDatabase.Callback(){ - - //override fun onOpen(db: SupportSQLiteDatabase) { - // super.onOpen(db) - // INSTANCE?.let { database -> - // scope.launch { - // populateDatabase(database.exerciseDao()) - // } - // } - // } - //suspend fun populateDatabase(exerciseDao: ExerciseDao) { - // Delete all content here. - //exerciseDao.deleteAll() - // Add sample words. - //var exercise = Exercise(0, "training", 10, 45 ) - // exerciseDao.insert(exercise) - - // TODO: Add your own words! - // } - //} - companion object { - @Volatile - private var INSTANCE: ExerciseDatabase? = null - fun getDatabase(context: Context, - scope: CoroutineScope - ): ExerciseDatabase { - val tempInstance = INSTANCE - if (tempInstance != null) { - return tempInstance - } - synchronized(this) { - val instance = Room.databaseBuilder( - context.applicationContext, - ExerciseDatabase::class.java, - "exercise_database" - ) - //.addCallback(ExerciseDatabaseCallback(scope)) - .build() - INSTANCE = instance - return instance - } - - } - } -} diff --git a/app/src/main/java/com/example/timert/data/entity/Exercise.kt b/app/src/main/java/com/example/timert/data/entity/Exercise.kt index 454c119..56dc021 100644 --- a/app/src/main/java/com/example/timert/data/entity/Exercise.kt +++ b/app/src/main/java/com/example/timert/data/entity/Exercise.kt @@ -1,11 +1,15 @@ package com.example.timert.data.entity import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.Index import androidx.room.PrimaryKey -@Entity(tableName = "exercise_table") +@Entity(tableName = "exercise_table", + indices = [Index("trainingId")]) data class Exercise(@PrimaryKey(autoGenerate = true) val id: Int = 0, val name_exercise: String, val num_sets: Int, - val rest_of_sets: Int) + val rest_of_sets: Int, + val trainingId: Int = 0) diff --git a/app/src/main/java/com/example/timert/data/entity/Training.kt b/app/src/main/java/com/example/timert/data/entity/Training.kt new file mode 100644 index 0000000..afe986e --- /dev/null +++ b/app/src/main/java/com/example/timert/data/entity/Training.kt @@ -0,0 +1,12 @@ +package com.example.timert.data.entity + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "training") +data class Training( + @PrimaryKey(autoGenerate = true) val id: Int = 0, + val name: String, + val numSets: Int, + val restBetweenSets: Int +) diff --git a/app/src/main/java/Workout.kt b/app/src/main/java/com/example/timert/data/entity/Workout.kt similarity index 62% rename from app/src/main/java/Workout.kt rename to app/src/main/java/com/example/timert/data/entity/Workout.kt index 3dd0900..0db0b68 100644 --- a/app/src/main/java/Workout.kt +++ b/app/src/main/java/com/example/timert/data/entity/Workout.kt @@ -1,17 +1,16 @@ -package com.example.timert +package com.example.timert.com.example.timert.data.entity import androidx.room.Entity import androidx.room.PrimaryKey @Entity(tableName = "workouts") data class Workout( - @PrimaryKey(autoGenerate = true) - val id: Int = 0, + @PrimaryKey(autoGenerate = true) val id: Int = 0, val name: String, val numApproaches: Int, val numExercises: Int, val restBetweenExercises: Int, val restBetweenApproaches: Int, val exerciseDuration: Int, - val startTime: Long? = null, - val endTime: Long? = null + //val startTime: Long? = null, + // val endTime: Long? = null ) \ No newline at end of file diff --git a/app/src/main/java/com/example/timert/data/repository/TrainingRepository.kt b/app/src/main/java/com/example/timert/data/repository/TrainingRepository.kt new file mode 100644 index 0000000..70c3b02 --- /dev/null +++ b/app/src/main/java/com/example/timert/data/repository/TrainingRepository.kt @@ -0,0 +1,23 @@ +package com.example.timert.data.repository + +import androidx.lifecycle.LiveData +import com.example.timert.data.dao.TrainingDao +import com.example.timert.data.entity.Training +import com.example.timert.data.trainingWithExercise + +class TrainingRepository(private val trainingDao: TrainingDao) { + + val allTrainings: LiveData> = trainingDao.getAllTrainings() + val allTrainingsWithExercises: LiveData> = trainingDao.getTrainingsWithExercises() + + suspend fun insert(training: Training): Long { + return trainingDao.insertTraining(training) + } + + suspend fun delete(training: Training) = trainingDao.deleteTraining(training) + + suspend fun deleteTrainingById(id: Int) { + trainingDao.deleteById(id) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/timert/data/repository/WorkoutRepository.kt b/app/src/main/java/com/example/timert/data/repository/WorkoutRepository.kt new file mode 100644 index 0000000..86d0ccb --- /dev/null +++ b/app/src/main/java/com/example/timert/data/repository/WorkoutRepository.kt @@ -0,0 +1,17 @@ +package com.example.timert.data.repository + +import androidx.lifecycle.LiveData +import com.example.timert.com.example.timert.data.dao.WorkoutDao +import com.example.timert.com.example.timert.data.entity.Workout + +class WorkoutRepository (private val workoutDao: WorkoutDao){ + val allWorkout: LiveData> = workoutDao.getAllWorkouts() + suspend fun insert(workout: Workout) { + workoutDao.insertWorkout(workout) + } + + suspend fun deleteWorkoutById(id: Int) { + workoutDao.deleteById(id) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/timert/data/trainingWithExercise.kt b/app/src/main/java/com/example/timert/data/trainingWithExercise.kt new file mode 100644 index 0000000..9067e1e --- /dev/null +++ b/app/src/main/java/com/example/timert/data/trainingWithExercise.kt @@ -0,0 +1,16 @@ +package com.example.timert.data + +import androidx.lifecycle.LiveData +import androidx.room.Embedded +import androidx.room.Relation +import com.example.timert.data.entity.Exercise +import com.example.timert.data.entity.Training + +data class trainingWithExercise ( + @Embedded val training: Training, + @Relation( + parentColumn = "id", + entityColumn = "trainingId" + ) + val exercises: List +) \ No newline at end of file diff --git a/app/src/main/java/com/example/timert/data/viewModel/ExerciseViewModel.kt b/app/src/main/java/com/example/timert/data/viewModel/ExerciseViewModel.kt index db28087..d3184b7 100644 --- a/app/src/main/java/com/example/timert/data/viewModel/ExerciseViewModel.kt +++ b/app/src/main/java/com/example/timert/data/viewModel/ExerciseViewModel.kt @@ -4,7 +4,7 @@ import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.viewModelScope -import com.example.timert.data.database.ExerciseDatabase +import com.example.timert.com.example.timert.data.database.AppDatabase import com.example.timert.data.entity.Exercise import com.example.timert.data.repository.ExerciseRepository import kotlinx.coroutines.launch @@ -14,7 +14,7 @@ class ExerciseViewModel(application: Application):AndroidViewModel(application){ val allExercise: LiveData> init{ - val exercisesDao = ExerciseDatabase.getDatabase(application, viewModelScope).exerciseDao() + val exercisesDao = AppDatabase.getDatabase(application).exerciseDao() repository = ExerciseRepository(exercisesDao) allExercise = repository.allExercise } diff --git a/app/src/main/java/com/example/timert/data/viewModel/TrainingViewModel.kt b/app/src/main/java/com/example/timert/data/viewModel/TrainingViewModel.kt new file mode 100644 index 0000000..18dec9e --- /dev/null +++ b/app/src/main/java/com/example/timert/data/viewModel/TrainingViewModel.kt @@ -0,0 +1,50 @@ +package com.example.timert.data.viewModel + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.viewModelScope +import com.example.timert.com.example.timert.data.database.AppDatabase +import com.example.timert.data.entity.Training + +import com.example.timert.data.repository.TrainingRepository +import com.example.timert.data.trainingWithExercise + +import kotlinx.coroutines.launch + +class TrainingViewModel (application: Application): AndroidViewModel(application){ + + val allWorkouts: LiveData> + private val trainingDao = AppDatabase.getDatabase(application).trainingDao() + private val repository = TrainingRepository(trainingDao) + + val allTrainings: LiveData> = repository.allTrainings + val allTrainingsWithExercises: LiveData> = repository.allTrainingsWithExercises + + init{ + val trainingDao = AppDatabase.getDatabase(application).trainingDao() + + allWorkouts = repository.allTrainings + } + fun insert(training: Training) = viewModelScope.launch { + repository.insert(training) + } + + fun deleteTrainingById(id: Int) = viewModelScope.launch { + repository.deleteTrainingById(id) + } + + fun delete(training: Training) = viewModelScope.launch { + repository.delete(training) + } + + fun insertTrainingAndLinkExercises(training: Training) { + viewModelScope.launch { + val newId = repository.insert(training) // возвращает id + // теперь обновляем trainingId в упражнениях + + AppDatabase.getDatabase(getApplication()).exerciseDao() + .updateExercisesWithTrainingId(newId.toInt()) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/timert/data/viewModel/WorkoutViewModel.kt b/app/src/main/java/com/example/timert/data/viewModel/WorkoutViewModel.kt new file mode 100644 index 0000000..e781fd3 --- /dev/null +++ b/app/src/main/java/com/example/timert/data/viewModel/WorkoutViewModel.kt @@ -0,0 +1,28 @@ +package com.example.timert.data.viewModel + +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.viewModelScope +import com.example.timert.com.example.timert.data.database.AppDatabase +import com.example.timert.com.example.timert.data.entity.Workout +import com.example.timert.data.repository.WorkoutRepository +import kotlinx.coroutines.launch + +class WorkoutViewModel(application: Application):AndroidViewModel(application){ + private val repository: WorkoutRepository + val allWorkouts: LiveData> + + init{ + val workoutDao = AppDatabase.getDatabase(application).workoutDao() + repository = WorkoutRepository(workoutDao) + allWorkouts = repository.allWorkout + } + fun insert(workout: Workout) = viewModelScope.launch { + repository.insert(workout) + } + + fun deleteWorkoutById(id: Int) = viewModelScope.launch { + repository.deleteWorkoutById(id) + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_add_workout.xml b/app/src/main/res/layout/activity_add_workout.xml index 965baf5..fd0c9f3 100644 --- a/app/src/main/res/layout/activity_add_workout.xml +++ b/app/src/main/res/layout/activity_add_workout.xml @@ -31,198 +31,255 @@ android:layout_marginTop="20dp" android:ems="10" android:inputType="text" - android:text="Тренировка 1" + android:text="Моя тренировка" android:textAlignment="center" android:textSize="20sp" /> - - - + tools:layout_editor_absoluteX="9dp"> - - - - - - - - - + android:orientation="vertical"> - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - +