From a00d3b34b05cd9898e7393425a4760190bce23ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=BC=D1=86=D0=BE=D0=B2=D0=B0=20=D0=9D=D0=B0=D0=B4?= =?UTF-8?q?=D0=B5=D0=B6=D0=B4=D0=B0?= Date: Mon, 12 May 2025 17:25:07 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D1=83=D0=BF=D1=80=D0=B0=D0=B6=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B9=20=D0=B4=D0=BB=D1=8F=20=D1=82=D1=80=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B8,=20=D0=BE=D1=82?= =?UTF-8?q?=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BD?= =?UTF-8?q?=D0=B0=20=D1=84=D0=BE=D1=80=D0=BC=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 10 +++ .../java/com/example/timert/AllTraining.kt | 57 +++++++++++++++-- .../com/example/timert/CreatingTraining.kt | 32 ++++++++++ .../main/java/com/example/timert/History.kt | 8 ++- .../timert/data/adapter/ExerciseAdapter.kt | 55 ++++++++++++++++ .../example/timert/data/dao/ExerciseDao.kt | 26 ++++++++ .../timert/data/database/ExerciseDatabase.kt | 61 ++++++++++++++++++ .../example/timert/data/entity/Exercise.kt | 11 ++++ .../data/repository/ExerciseRepository.kt | 17 +++++ .../data/viewModel/ExerciseViewModel.kt | 28 +++++++++ .../main/res/layout/activity_all_training.xml | 14 ++--- .../res/layout/activity_creating_training.xml | 62 +++++++++++++------ app/src/main/res/layout/activity_main.xml | 2 +- app/src/main/res/layout/resyclerview_item.xml | 39 ++++++++++++ gradle.properties | 3 +- 15 files changed, 392 insertions(+), 33 deletions(-) create mode 100644 app/src/main/java/com/example/timert/data/adapter/ExerciseAdapter.kt create mode 100644 app/src/main/java/com/example/timert/data/dao/ExerciseDao.kt create mode 100644 app/src/main/java/com/example/timert/data/database/ExerciseDatabase.kt create mode 100644 app/src/main/java/com/example/timert/data/entity/Exercise.kt create mode 100644 app/src/main/java/com/example/timert/data/repository/ExerciseRepository.kt create mode 100644 app/src/main/java/com/example/timert/data/viewModel/ExerciseViewModel.kt create mode 100644 app/src/main/res/layout/resyclerview_item.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 74303cb..f2c6a58 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,6 +1,7 @@ plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) + id("kotlin-kapt") } android { @@ -45,4 +46,13 @@ dependencies { testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) + implementation("androidx.room:room-runtime:2.7.1") + implementation("androidx.room:room-ktx:2.7.1") + kapt ("androidx.room:room-compiler:2.7.1") + implementation ("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0") + implementation ("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0") + + + + } \ No newline at end of file diff --git a/app/src/main/java/com/example/timert/AllTraining.kt b/app/src/main/java/com/example/timert/AllTraining.kt index e25092d..d18283b 100644 --- a/app/src/main/java/com/example/timert/AllTraining.kt +++ b/app/src/main/java/com/example/timert/AllTraining.kt @@ -1,36 +1,78 @@ package com.example.timert +import android.app.Activity import android.app.AlertDialog import android.content.Intent import android.os.Bundle +import android.text.TextUtils 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.data.entity.Exercise +import com.example.timert.data.viewModel.ExerciseViewModel class AllTraining : AppCompatActivity() { private var isSave = false - + private lateinit var editExerciseView: EditText + private lateinit var numExerciseView: EditText + private lateinit var numRestExerciseView: EditText override fun onCreate(savedInstanceState: Bundle?) { + val exerciseViewModel = ViewModelProvider( + this, + ViewModelProvider.AndroidViewModelFactory.getInstance(application) + )[ExerciseViewModel::class.java] super.onCreate(savedInstanceState) enableEdgeToEdge() setContentView(R.layout.activity_all_training) + editExerciseView = findViewById(R.id.name_training) + numExerciseView = findViewById(R.id.time_work) + numRestExerciseView = findViewById(R.id.rest_workout) + 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 } + val buttonSaveAll: Button = findViewById(R.id.btn_save_all_vse) buttonSaveAll.setOnClickListener { - val intent = Intent(this, CreatingTraining::class.java) - startActivity(intent) - isSave = true + val exerciseName = editExerciseView.text.toString() + val numSetsText = numExerciseView.text.toString() + val restSetsText = numRestExerciseView.text.toString() + + if (exerciseName.isBlank() || numSetsText.isBlank() || restSetsText.isBlank()) { + Toast.makeText(this, "Заполните все поля", Toast.LENGTH_SHORT).show() + } else { + val numSets = numSetsText.toIntOrNull() + val restSets = restSetsText.toIntOrNull() + + if (numSets == null || restSets == null) { + Toast.makeText(this, "Неверный формат чисел", Toast.LENGTH_SHORT).show() + return@setOnClickListener + } + val newExercise = Exercise( + name_exercise = exerciseName, + num_sets = numSets, + rest_of_sets = restSets + ) + exerciseViewModel.insert(newExercise) + Toast.makeText(this, "Упражнение сохранено", Toast.LENGTH_SHORT).show() + isSave = true + finish() + } + + + } + val button_on_main_all: Button = findViewById(R.id.btn_on_main_all_vse) button_on_main_all.setOnClickListener { if (isSave == false) { @@ -41,6 +83,7 @@ class AllTraining : AppCompatActivity() { val intent = Intent(this, MainActivity::class.java) startActivity(intent) + finish() } .setNegativeButton("Нет") { dialog, _ -> dialog.dismiss() @@ -63,6 +106,12 @@ class AllTraining : AppCompatActivity() { setupPlusMinus(timeInputRest, btnPlusTime, btnMinusTime, minValue = 5, maxValue = 600) setupPlusMinus(setsInput, btnPlusSets, btnMinusSets, minValue = 5, maxValue = 600) } + + companion object { + const val EXTRA_REPLY = "com.example.android.wordlistsql.REPLY" + } + + fun setupPlusMinus( editText: EditText, plus: ImageView, diff --git a/app/src/main/java/com/example/timert/CreatingTraining.kt b/app/src/main/java/com/example/timert/CreatingTraining.kt index 963d140..cafa793 100644 --- a/app/src/main/java/com/example/timert/CreatingTraining.kt +++ b/app/src/main/java/com/example/timert/CreatingTraining.kt @@ -11,13 +11,45 @@ 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.data.adapter.ExerciseAdapter +import com.example.timert.data.viewModel.ExerciseViewModel class CreatingTraining : AppCompatActivity() { + + private lateinit var viewModel: ExerciseViewModel + private var isSave = false override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContentView(R.layout.activity_creating_training) + + + + + viewModel = ViewModelProvider( + this, + ViewModelProvider.AndroidViewModelFactory.getInstance(application) + )[ExerciseViewModel::class.java] + + + val adapter = ExerciseAdapter { id -> + viewModel.deleteExerciseById(id) + } + // adapter = ExerciseAdapter(this) + val recyclerView = findViewById(R.id.exercise) + recyclerView.adapter = adapter + recyclerView.layoutManager = LinearLayoutManager(this) + + + viewModel.allExercise.observe(this) { exercises -> + exercises?.let { + adapter.setExercises(it) + } + } 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) diff --git a/app/src/main/java/com/example/timert/History.kt b/app/src/main/java/com/example/timert/History.kt index 5e38a06..2bfbb68 100644 --- a/app/src/main/java/com/example/timert/History.kt +++ b/app/src/main/java/com/example/timert/History.kt @@ -7,12 +7,18 @@ 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.data.adapter.ExerciseAdapter +import com.example.timert.data.viewModel.ExerciseViewModel class History : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() - setContentView(R.layout.activity_history) + 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) 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 new file mode 100644 index 0000000..d19ea94 --- /dev/null +++ b/app/src/main/java/com/example/timert/data/adapter/ExerciseAdapter.kt @@ -0,0 +1,55 @@ +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(){ +class ExerciseAdapter( + private val onDeleteClick: (Int) -> Unit +) : RecyclerView.Adapter() { + private var exerciseList: List = emptyList() + + + + + inner class ExerciseViewHolder(itemView: View):RecyclerView.ViewHolder(itemView){ + val textViewExercise: TextView = itemView.findViewById(R.id.textView13) + val deleteButton: ImageButton = itemView.findViewById(R.id.deleteButton) + } + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ExerciseViewHolder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.resyclerview_item, parent, false) + return ExerciseViewHolder(view) + } + + override fun onBindViewHolder(holder: ExerciseViewHolder, position: Int) { + + val exercise = exerciseList[position] + + holder.textViewExercise.text = + "${exercise.name_exercise} — работа: ${exercise.num_sets} сек, отдых: ${exercise.rest_of_sets} сек" + holder.deleteButton.setOnClickListener { + onDeleteClick(exercise.id) + } + } + + override fun getItemCount() = exerciseList.size + + fun setExercises(exercises: List) { + this.exerciseList = exercises + 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 new file mode 100644 index 0000000..95eef78 --- /dev/null +++ b/app/src/main/java/com/example/timert/data/dao/ExerciseDao.kt @@ -0,0 +1,26 @@ +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 com.example.timert.data.entity.Exercise + + +@Dao +interface ExerciseDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert(exercise: Exercise) + + @Query("SELECT * FROM exercise_table") + fun getExercise(): LiveData> + + + //@Delete + // suspend fun delete(exercise: Exercise) + + @Query("DELETE FROM exercise_table 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/ExerciseDatabase.kt b/app/src/main/java/com/example/timert/data/database/ExerciseDatabase.kt new file mode 100644 index 0000000..813180e --- /dev/null +++ b/app/src/main/java/com/example/timert/data/database/ExerciseDatabase.kt @@ -0,0 +1,61 @@ +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 new file mode 100644 index 0000000..454c119 --- /dev/null +++ b/app/src/main/java/com/example/timert/data/entity/Exercise.kt @@ -0,0 +1,11 @@ +package com.example.timert.data.entity + +import androidx.room.Entity +import androidx.room.PrimaryKey + + +@Entity(tableName = "exercise_table") +data class Exercise(@PrimaryKey(autoGenerate = true) val id: Int = 0, + val name_exercise: String, + val num_sets: Int, + val rest_of_sets: Int) diff --git a/app/src/main/java/com/example/timert/data/repository/ExerciseRepository.kt b/app/src/main/java/com/example/timert/data/repository/ExerciseRepository.kt new file mode 100644 index 0000000..44e1640 --- /dev/null +++ b/app/src/main/java/com/example/timert/data/repository/ExerciseRepository.kt @@ -0,0 +1,17 @@ +package com.example.timert.data.repository + +import androidx.lifecycle.LiveData +import com.example.timert.data.dao.ExerciseDao +import com.example.timert.data.entity.Exercise + +class ExerciseRepository(private val exerciseDao:ExerciseDao){ + val allExercise: LiveData> = exerciseDao.getExercise() + suspend fun insert(training: Exercise) { + exerciseDao.insert(training) + } + + suspend fun deleteExerciseById(id: Int) { + exerciseDao.deleteById(id) + } + +} \ 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 new file mode 100644 index 0000000..db28087 --- /dev/null +++ b/app/src/main/java/com/example/timert/data/viewModel/ExerciseViewModel.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.data.database.ExerciseDatabase +import com.example.timert.data.entity.Exercise +import com.example.timert.data.repository.ExerciseRepository +import kotlinx.coroutines.launch + +class ExerciseViewModel(application: Application):AndroidViewModel(application){ + private val repository: ExerciseRepository + val allExercise: LiveData> + + init{ + val exercisesDao = ExerciseDatabase.getDatabase(application, viewModelScope).exerciseDao() + repository = ExerciseRepository(exercisesDao) + allExercise = repository.allExercise + } + fun insert(exercise: Exercise) = viewModelScope.launch { + repository.insert(exercise) + } + + fun deleteExerciseById(id: Int) = viewModelScope.launch { + repository.deleteExerciseById(id) + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_all_training.xml b/app/src/main/res/layout/activity_all_training.xml index bb73104..cbf1331 100644 --- a/app/src/main/res/layout/activity_all_training.xml +++ b/app/src/main/res/layout/activity_all_training.xml @@ -11,7 +11,7 @@ @@ -31,7 +31,7 @@ android:layout_marginTop="20dp" android:ems="10" android:inputType="text" - android:text="Упражнение 1" + android:text="Упражнение" android:textAlignment="center" android:textSize="20sp" /> @@ -40,8 +40,8 @@ @@ -50,7 +50,7 @@ android:id="@+id/textView3" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="Время работы" + android:text="Время работы, сек" android:textAlignment="center" android:textSize="24sp" /> @@ -93,13 +93,13 @@ android:id="@+id/textView8_all" android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="Отдых между упражнениями" + android:text="Отдых между упражнениями, сек" android:textAlignment="center" android:textSize="24sp" /> @@ -28,10 +28,9 @@ android:id="@+id/name_training" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="20dp" android:ems="10" android:inputType="text" - android:text="Тренировка 1" + android:text="Моя тренировка" android:textAlignment="center" android:textSize="20sp" /> @@ -40,11 +39,12 @@ + app:layout_constraintBottom_toTopOf="@+id/exercise" + app:layout_constraintTop_toBottomOf="@+id/linearLayout"> + + + + + - + app:layout_constraintVertical_bias="0.178" /> + +