Merge pull request 'Созданы звуковые уведомления, создана room, AppDatabase, Workout.kt, WorkoutAdapter.kt, WorkoutDao.kt, MyApplication.kt, workout_item.kt' (#3) from stud179289/Timer:Novikov_features into main

Reviewed-on: #3
This commit is contained in:
Надежда Емцова 2025-05-13 15:51:37 +00:00
commit 505f533f04
18 changed files with 283 additions and 38 deletions

View File

@ -1,7 +1,8 @@
plugins { plugins {
alias(libs.plugins.android.application) alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.android)
id("kotlin-kapt") id("org.jetbrains.kotlin.kapt")
id("androidx.room")
} }
android { android {
@ -34,6 +35,13 @@ android {
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = "11"
} }
buildFeatures {
//noinspection WrongGradleMethod
room {
schemaDirectory("$projectDir/schemas")
}
}
} }
dependencies { dependencies {
@ -46,13 +54,13 @@ dependencies {
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core) 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")
// Room dependencies
implementation("androidx.room:room-runtime:2.7.1") // Замените на вашу версию
implementation("androidx.room:room-ktx:2.7.1") // Замените на вашу версию
kapt("androidx.room:room-compiler:2.7.1") // Замените на вашу версию
// Lifecycle dependencies
implementation ("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0") // Замените на вашу версию
} implementation ("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0") // Замените на вашу версию
}

View File

@ -0,0 +1,77 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "87cf8dcd07bc0b2c0a8d52dbb0eca913",
"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, `startTime` INTEGER, `endTime` INTEGER)",
"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
},
{
"fieldPath": "startTime",
"columnName": "startTime",
"affinity": "INTEGER"
},
{
"fieldPath": "endTime",
"columnName": "endTime",
"affinity": "INTEGER"
}
],
"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, '87cf8dcd07bc0b2c0a8d52dbb0eca913')"
]
}
}

View File

@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<application <application
android:name=".MyApplication"
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"

View File

@ -0,0 +1,9 @@
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
}

View File

@ -0,0 +1,17 @@
package com.example.timert
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "workouts")
data class Workout(
@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
)

View File

@ -0,0 +1,27 @@
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<Workout>) : RecyclerView.Adapter<WorkoutAdapter.WorkoutViewHolder>() {
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
}

View File

@ -0,0 +1,22 @@
package com.example.timert
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
@Dao
interface WorkoutDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertWorkout(workout: Workout): Long
@Query("SELECT * FROM workouts")
suspend fun getAllWorkouts(): List<Workout>
@Query("SELECT * FROM workouts WHERE id = :workoutId")
suspend fun getWorkoutById(workoutId: Int): Workout?
@Update
suspend fun updateWorkout(workout: Workout)
}

View File

@ -7,27 +7,24 @@ import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.example.timert.data.adapter.ExerciseAdapter import kotlinx.coroutines.CoroutineScope
import com.example.timert.data.viewModel.ExerciseViewModel import kotlinx.coroutines.Dispatchers
class History : AppCompatActivity() { class History : AppCompatActivity() {
private lateinit var recyclerViewHistory: RecyclerView
private val coroutineScope = CoroutineScope(Dispatchers.IO)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
setContentView(R.layout.activity_history)
recyclerViewHistory = findViewById(R.id.recyclerViewHistory)
recyclerViewHistory.layoutManager = LinearLayoutManager(this)
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 button_on_main: Button = findViewById(R.id.btn_on_main)
button_on_main.setOnClickListener {
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
} }
} }

View File

@ -2,7 +2,7 @@ package com.example.timert
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.View import android.util.Log
import android.widget.Button import android.widget.Button
import android.widget.Toast import android.widget.Toast
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
@ -10,7 +10,11 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
@ -50,9 +54,8 @@ class MainActivity : AppCompatActivity() {
//TODO: Intent для открытия Activity создания шаблона (форма 7) //TODO: Intent для открытия Activity создания шаблона (форма 7)
val intent = Intent(this, History::class.java) val intent = Intent(this, History::class.java)
startActivity(intent) startActivity(intent)
} }
// ---- ROOM TEST ----
} }
} }

View File

@ -0,0 +1,20 @@
package com.example.timert
import android.app.Application
import androidx.room.Room
import com.example.timert.AppDatabase
class MyApplication : Application() {
companion object {
lateinit var database: AppDatabase
}
override fun onCreate() {
super.onCreate()
database = Room.databaseBuilder(
applicationContext,
AppDatabase::class.java,
"workout_database"
).build()
}
}

View File

@ -2,10 +2,9 @@ package com.example.timert
import android.app.AlertDialog import android.app.AlertDialog
import android.content.Intent import android.content.Intent
import android.media.Image import android.media.MediaPlayer
import android.os.Bundle import android.os.Bundle
import android.os.CountDownTimer import android.os.CountDownTimer
import android.widget.Button
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.TextView import android.widget.TextView
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
@ -28,6 +27,14 @@ class timer : AppCompatActivity() {
private var exerciseDuration = 45 // например, 45 секунд упражнение private var exerciseDuration = 45 // например, 45 секунд упражнение
private var currentTimer: CountDownTimer? = null private var currentTimer: CountDownTimer? = null
private var timeLeftInMillis: Long = 0 private var timeLeftInMillis: Long = 0
// Объявляем переменные для MediaPlayer
private var startWorkoutSound: MediaPlayer? = null
private var workSound: MediaPlayer? = null
private var startRestSound: MediaPlayer? = null
private var restSound: MediaPlayer? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
@ -47,6 +54,12 @@ class timer : AppCompatActivity() {
restBetweenApproaches = intent.getIntExtra("restBetweenApproaches", restBetweenApproaches) restBetweenApproaches = intent.getIntExtra("restBetweenApproaches", restBetweenApproaches)
exerciseDuration = intent.getIntExtra("exerciseDuration", exerciseDuration) exerciseDuration = intent.getIntExtra("exerciseDuration", exerciseDuration)
// Инициализируем MediaPlayer
startWorkoutSound = MediaPlayer.create(this, R.raw.sound1)
workSound = MediaPlayer.create(this, R.raw.sound1)
startRestSound = MediaPlayer.create(this, R.raw.sound2)
restSound = MediaPlayer.create(this, R.raw.sound3)
startTraining() startTraining()
val button: ImageButton = findViewById(R.id.stop) val button: ImageButton = findViewById(R.id.stop)
@ -106,15 +119,18 @@ class timer : AppCompatActivity() {
statusText.text = "Тренировка завершена!" statusText.text = "Тренировка завершена!"
rootLayout.setBackgroundResource(R.drawable.blue_gradient) rootLayout.setBackgroundResource(R.drawable.blue_gradient)
timerText.text = "00:00" timerText.text = "00:00"
// Здесь должен быть звук завершения тренировки, если нужно
return return
} }
statusSet.text = "Подход № $currentApproach"
var currentExercise = 1 var currentExercise = 1
fun nextExercise() { fun nextExercise() {
if (currentExercise > numExercises) { if (currentExercise > numExercises) {
statusText.text = "Отдых между подходами" statusText.text = "Отдых между подходами"
rootLayout.setBackgroundResource(R.drawable.green_gradient) rootLayout.setBackgroundResource(R.drawable.green_gradient)
// Звук перед началом отдыха между подходами
startRestSound?.start()
startRest(restBetweenApproaches) { startRest(restBetweenApproaches) {
currentApproach++ currentApproach++
nextApproach() nextApproach()
@ -122,23 +138,28 @@ class timer : AppCompatActivity() {
return return
} }
rootLayout.setBackgroundResource(R.drawable.gradient_pink) rootLayout.setBackgroundResource(R.drawable.gradient_pink)
statusText.text="Работа" statusText.text = "Работа"
statusExercise.text = "$currentExercise / $numExercises" statusExercise.text = "$currentExercise / $numExercises"
statusSet.text="$currentApproach" statusSet.text = "$currentApproach"
// Звук перед началом упражнения
workSound?.start()
startTimer(exerciseDuration) { startTimer(exerciseDuration) {
statusText.text = "Отдых" statusText.text = "Отдых"
rootLayout.setBackgroundResource(R.drawable.green_gradient) rootLayout.setBackgroundResource(R.drawable.green_gradient)
// Звук перед началом отдыха между упражнениями
restSound?.start()
startRest(restBetweenExercises) { startRest(restBetweenExercises) {
currentExercise++ currentExercise++
nextExercise() nextExercise()
} }
} }
} }
// Звук перед началом подхода
startWorkoutSound?.start()
nextExercise() nextExercise()
} }
nextApproach() nextApproach()
} }
@ -162,7 +183,6 @@ class timer : AppCompatActivity() {
override fun onTick(millisUntilFinished: Long) { override fun onTick(millisUntilFinished: Long) {
timerText.text = "${millisUntilFinished / 1000}" timerText.text = "${millisUntilFinished / 1000}"
statusText.text = "Отдых" statusText.text = "Отдых"
} }
override fun onFinish() { override fun onFinish() {
@ -170,5 +190,18 @@ class timer : AppCompatActivity() {
} }
}.start() }.start()
} }
override fun onDestroy() {
super.onDestroy()
currentTimer?.cancel()
// Освобождаем ресурсы MediaPlayer
startWorkoutSound?.release()
workSound?.release()
startRestSound?.release()
restSound?.release()
startWorkoutSound = null
workSound = null
startRestSound = null
restSound = null
}
} }

View File

@ -8,6 +8,15 @@
android:background="@drawable/gradient_purpure" android:background="@drawable/gradient_purpure"
tools:context=".History"> tools:context=".History">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerViewHistory"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<TextView <TextView
android:id="@+id/textView4" android:id="@+id/textView4"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardCornerRadius="4dp">
<TextView
android:id="@+id/textViewName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:text="Workout Name"
android:textSize="18sp" />
</androidx.cardview.widget.CardView>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,5 @@
[versions] [versions]
agp = "8.9.2" agp = "8.10.0"
kotlin = "2.0.21" kotlin = "2.0.21"
coreKtx = "1.16.0" coreKtx = "1.16.0"
junit = "4.13.2" junit = "4.13.2"

View File

@ -10,6 +10,12 @@ pluginManagement {
mavenCentral() mavenCentral()
gradlePluginPortal() gradlePluginPortal()
} }
plugins {
id("com.android.application") version "8.3.2" apply false // Замените на вашу версию
id("org.jetbrains.kotlin.android") version "1.9.0" apply false // Замените на вашу версию Kotlin
id("org.jetbrains.kotlin.kapt") version "1.9.0" apply false // замените на вашу версию Kotlin
id("androidx.room") version "2.7.1" apply false // Добавьте эту строку, заменив на вашу версию Room
}
} }
dependencyResolutionManagement { dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
@ -20,5 +26,4 @@ dependencyResolutionManagement {
} }
rootProject.name = "TimerT" rootProject.name = "TimerT"
include(":app") include(":app")