This commit is contained in:
ksenianeva 2023-11-22 09:10:15 +04:00
parent 86be093a35
commit cb425c227b
13 changed files with 324 additions and 70 deletions

View File

@ -1,6 +1,7 @@
plugins { plugins {
id("com.android.application") id("com.android.application")
id("org.jetbrains.kotlin.android") id("org.jetbrains.kotlin.android")
id("com.google.devtools.ksp")
} }
android { android {
@ -9,7 +10,7 @@ android {
defaultConfig { defaultConfig {
applicationId = "com.example.myapplication" applicationId = "com.example.myapplication"
minSdk = 24 minSdk = 26
targetSdk = 33 targetSdk = 33
versionCode = 1 versionCode = 1
versionName = "1.0" versionName = "1.0"
@ -27,17 +28,17 @@ android {
} }
} }
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_17
} }
kotlinOptions { kotlinOptions {
jvmTarget = "1.8" jvmTarget = "17"
} }
buildFeatures { buildFeatures {
compose = true compose = true
} }
composeOptions { composeOptions {
kotlinCompilerExtensionVersion = "1.4.3" kotlinCompilerExtensionVersion = "1.4.5"
} }
packaging { packaging {
resources { resources {
@ -46,9 +47,16 @@ android {
} }
} }
kotlin {
jvmToolchain(17)
}
dependencies { dependencies {
//Core
implementation("androidx.core:core-ktx:1.9.0") implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2") implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
//UI
implementation("androidx.activity:activity-compose:1.7.2") implementation("androidx.activity:activity-compose:1.7.2")
implementation(platform("androidx.compose:compose-bom:2023.03.00")) implementation(platform("androidx.compose:compose-bom:2023.03.00"))
implementation("androidx.navigation:navigation-compose:2.6.0") implementation("androidx.navigation:navigation-compose:2.6.0")
@ -57,6 +65,16 @@ dependencies {
implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview") implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3") implementation("androidx.compose.material3:material3")
// Room
val room_version = "2.5.2"
implementation("androidx.room:room-runtime:$room_version")
annotationProcessor("androidx.room:room-compiler:$room_version")
ksp("androidx.room:room-compiler:$room_version")
implementation("androidx.room:room-ktx:$room_version")
implementation("androidx.room:room-paging:$room_version")
//Tests
testImplementation("junit:junit:4.13.2") testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")

View File

@ -10,22 +10,40 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavController import androidx.navigation.NavController
import com.example.myapplication.category.model.getTestCategory import com.example.myapplication.category.model.Category
import com.example.myapplication.database.AppDatabase
import com.example.myapplication.ui.theme.MyApplicationTheme import com.example.myapplication.ui.theme.MyApplicationTheme
import com.example.myapplication.user.model.User
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable @Composable
fun CategoriesList(navController: NavController?) { fun CategoriesList(navController: NavController?) {
val context = LocalContext.current;
val categories = remember { mutableStateListOf<Category>() }
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
AppDatabase.getInstance(context).categoryDao().getAll().collect { data ->
categories.clear()
categories.addAll(data)
}
}
}
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier modifier = Modifier
.padding(all = 10.dp) .padding(all = 10.dp)
.verticalScroll(rememberScrollState())) { .verticalScroll(rememberScrollState())) {
getTestCategory().forEachIndexed() { _, category -> categories.forEachIndexed() { _, category ->
Row(Modifier.padding(all = 20.dp)) { Row(Modifier.padding(all = 20.dp)) {
Text(category.category_name) Text(category.category_name)
} }

View File

@ -0,0 +1,27 @@
package com.example.myapplication.category.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Delete
import androidx.room.Update
import com.example.myapplication.category.model.Category
import kotlinx.coroutines.flow.Flow
@Dao
interface CategoryDao {
@Query("select * from categories order by category_name collate nocase asc")
fun getAll(): Flow<List<Category>>
@Query("select * from categories where categories.uid = :uid")
suspend fun getByUid(uid: Int): Category
@Insert
suspend fun insert(category: Category)
@Update
suspend fun update(category: Category)
@Delete
suspend fun delete(category: Category)
}

View File

@ -1,15 +1,11 @@
package com.example.myapplication.category.model package com.example.myapplication.category.model
import java.io.Serializable import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "categories")
data class Category( data class Category(
@PrimaryKey val uid: Int,
val category_name: String, val category_name: String,
) : Serializable )
fun getTestCategory(): List<Category> {
return listOf(
Category("Отжимания"),
Category("Кардио"),
Category("Силовые")
)
}

View File

@ -0,0 +1,27 @@
package com.example.myapplication.challenge.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Delete
import androidx.room.Update
import com.example.myapplication.challenge.model.Challenge
import kotlinx.coroutines.flow.Flow
@Dao
interface ChallengeDao {
@Query("select * from challenges order by challenge_name asc")
fun getAll(): Flow<List<Challenge>>
@Query("select * from challenges where challenges.challenge_id = :uid")
suspend fun getByUid(uid: Int): Challenge
@Insert
suspend fun insert(challenge: Challenge)
@Update
suspend fun update(challenge: Challenge)
@Delete
suspend fun delete(challenge: Challenge)
}

View File

@ -1,19 +1,33 @@
package com.example.myapplication.challenge.model package com.example.myapplication.challenge.model
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
import com.example.myapplication.category.model.Category import com.example.myapplication.category.model.Category
import java.io.Serializable import com.example.myapplication.user.model.User
@Entity(tableName = "challenges", foreignKeys = [
ForeignKey(
entity = User::class,
parentColumns = ["uid"],
childColumns = ["user_id"],
onDelete = ForeignKey.RESTRICT,
onUpdate = ForeignKey.RESTRICT,
),
ForeignKey(
entity = Category::class,
parentColumns = ["uid"],
childColumns = ["category_id"],
onDelete = ForeignKey.RESTRICT,
onUpdate = ForeignKey.RESTRICT,
),
])
data class Challenge( data class Challenge(
@PrimaryKey val challenge_id: Int,
val challenge_name: String, val challenge_name: String,
val challenge_status: Boolean, val challenge_status: Boolean,
val category: Category, val category_id: Int,
) : Serializable @ColumnInfo(index = true)
val user_id: Int,
fun getTestChallenge(): List<Challenge> { )
val сategory = Category("Отжимания")
return listOf(
Challenge("Сделать 20 отжиманий", false, сategory),
Challenge("Сделать 10 отжиманий", false, сategory),
Challenge("Сделать 5 отжиманий", false, сategory),
)
}

View File

@ -0,0 +1,79 @@
package com.example.myapplication.database
import java.util.concurrent.Flow
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import com.example.myapplication.category.dao.CategoryDao
import com.example.myapplication.category.model.Category
import com.example.myapplication.user.model.UserWithChallenges
import com.example.myapplication.challenge.dao.ChallengeDao
import com.example.myapplication.challenge.model.Challenge
import com.example.myapplication.user.dao.UserDao
import com.example.myapplication.user.model.User
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.time.LocalDate
@Database(entities = [User::class, Challenge::class, Category::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun challengeDao(): ChallengeDao
abstract fun categoryDao(): CategoryDao
abstract fun userDao(): UserDao
companion object {
private const val DB_NAME: String = "sport-app-db"
@Volatile
private var INSTANCE: AppDatabase? = null
private suspend fun populateDatabase() {
INSTANCE?.let { database ->
val categoryDao = database.categoryDao()
val category1 = Category(3, "Кардио")
val category2 = Category(4, "Силовые тренировки")
categoryDao.insert(category1)
categoryDao.insert(category2)
val userDao = database.userDao()
val user1 = User(3, "test1", "1234", "John")
val user2 = User(4, "test2", "1234", "Ivan")
userDao.insert(user1)
userDao.insert(user2)
val challengeDao = database.challengeDao()
val challenge1 = Challenge(3, "Сделать 10 отжиманий", false, category2.uid, user1.uid)
val challenge2 = Challenge(4, "Пробежать 1 км", true, category1.uid, user2.uid)
challengeDao.insert(challenge1)
challengeDao.insert(challenge2)
}
}
fun getInstance(appContext: Context): AppDatabase {
return INSTANCE ?: synchronized(this) {
Room.databaseBuilder(
appContext,
AppDatabase::class.java,
DB_NAME
)
.addCallback(object : RoomDatabase.Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
CoroutineScope(Dispatchers.IO).launch {
populateDatabase()
}
}
})
.build()
.also { INSTANCE = it }
}
}
}
}

View File

@ -13,47 +13,69 @@ import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.example.myapplication.user.model.getTestUser
import com.example.myapplication.ui.theme.MyApplicationTheme import com.example.myapplication.ui.theme.MyApplicationTheme
import com.example.myapplication.R import com.example.myapplication.R
import com.example.myapplication.database.AppDatabase
import com.example.myapplication.user.model.UserWithChallenges
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun UserView(id: Int) { fun UserView(id: Int) {
var user = getTestUser()[id] val context = LocalContext.current
val (userWithChallanges, setUserWithChallanges) = remember { mutableStateOf<UserWithChallenges?>(null) }
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
setUserWithChallanges(AppDatabase.getInstance(context).userDao().getByUid(id))
}
}
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier modifier = Modifier
.padding(all = 10.dp) .padding(all = 10.dp)
.verticalScroll(rememberScrollState())) { .verticalScroll(rememberScrollState())
OutlinedTextField(modifier = Modifier.fillMaxWidth(), value = user.login, onValueChange = {}, readOnly = true, ) {
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = userWithChallanges?.user?.login.toString(),
onValueChange = {},
readOnly = true,
label = { label = {
Text(stringResource(id = R.string.user_login)) Text(stringResource(id = R.string.user_login))
} }
) )
OutlinedTextField(modifier = Modifier.fillMaxWidth(), value = user.fio, onValueChange = {}, readOnly = true, OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = userWithChallanges?.user?.fio.toString(),
onValueChange = {},
readOnly = true,
label = { label = {
Text(stringResource(id = R.string.user_fio)) Text(stringResource(id = R.string.user_fio))
} }
) )
user.challenges.forEachIndexed() { _, challenge -> userWithChallanges?.challenges?.forEachIndexed() { _, challenge ->
Row { Row {
Text(text = challenge.challenge_name) Text(text = challenge.challenge_name)
if (challenge.challenge_status) { if (challenge.challenge_status) {
Text(text = " - Выполнено") Text(text = " - Выполнено")
} } else {
else {
Text(text = " - В процессе") Text(text = " - В процессе")
} }
} }
} }
} }
} }
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO) @Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) @Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)

View File

@ -12,24 +12,42 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.navigation.NavController import androidx.navigation.NavController
import com.example.myapplication.composeui.navigation.Screen import com.example.myapplication.composeui.navigation.Screen
import com.example.myapplication.user.model.getTestUser import com.example.myapplication.database.AppDatabase
import com.example.myapplication.ui.theme.MyApplicationTheme import com.example.myapplication.ui.theme.MyApplicationTheme
import com.example.myapplication.user.model.User
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.withContext
@Composable @Composable
fun UsersList(navController: NavController?) { fun UsersList(navController: NavController?) {
val context = LocalContext.current;
val users = remember { mutableStateListOf<User>() }
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
AppDatabase.getInstance(context).userDao().getAll().collect { data ->
users.clear()
users.addAll(data)
}
}
}
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier modifier = Modifier
.padding(all = 10.dp) .padding(all = 10.dp)
.verticalScroll(rememberScrollState())) { .verticalScroll(rememberScrollState())) {
getTestUser().forEachIndexed() { _, user -> users.forEachIndexed() { _, user ->
val userId = Screen.UserView.route.replace("{id}", (user.id).toString()) val userId = Screen.UserView.route.replace("{id}", (user.uid).toString())
Row(Modifier.padding(all = 10.dp)) { Row(Modifier.padding(all = 10.dp)) {
Button( Button(
modifier = Modifier modifier = Modifier

View File

@ -0,0 +1,28 @@
package com.example.myapplication.user.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.example.myapplication.user.model.User
import com.example.myapplication.user.model.UserWithChallenges
import kotlinx.coroutines.flow.Flow
@Dao
interface UserDao {
@Query("select * from users order by fio asc")
fun getAll(): Flow<List<User>>
@Query("select * from users where users.uid = :uid")
suspend fun getByUid(uid: Int): UserWithChallenges
@Insert
suspend fun insert(user: User)
@Update
suspend fun update(user: User)
@Delete
suspend fun delete(user: User)
}

View File

@ -1,22 +1,13 @@
package com.example.myapplication.user.model package com.example.myapplication.user.model
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.example.myapplication.challenge.model.Challenge import com.example.myapplication.challenge.model.Challenge
import com.example.myapplication.challenge.model.getTestChallenge
import java.io.Serializable
@Entity(tableName = "users")
data class User( data class User(
val id: Int, @PrimaryKey val uid: Int,
val login: String, val login: String,
val password: String, val password: String,
val fio: String, val fio: String,
val challenges: List<Challenge>, )
) : Serializable
fun getTestUser(): List<User> {
val challenges = getTestChallenge()
return listOf(
User(0,"user1", "1234", "ivanov ivan", challenges),
User(1,"user2", "1234", "vasiliev ivan", challenges),
User(2,"user3", "1234", "listov ivan", challenges),
)
}

View File

@ -0,0 +1,15 @@
package com.example.myapplication.user.model
import androidx.room.Embedded
import androidx.room.Relation
import com.example.myapplication.challenge.model.Challenge
import com.example.myapplication.user.model.User
data class UserWithChallenges(
@Embedded val user: User,
@Relation(
parentColumn = "uid",
entityColumn = "user_id"
)
val challenges: List<Challenge>
)

View File

@ -2,4 +2,5 @@
plugins { plugins {
id("com.android.application") version "8.1.1" apply false id("com.android.application") version "8.1.1" apply false
id("org.jetbrains.kotlin.android") version "1.8.10" apply false id("org.jetbrains.kotlin.android") version "1.8.10" apply false
id("com.google.devtools.ksp") version "1.8.20-1.0.11" apply false
} }