Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
cf2d21d2eb | |||
|
cb425c227b | ||
|
86be093a35 |
@ -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,16 +47,34 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
kotlin {
|
||||||
|
jvmToolchain(17)
|
||||||
|
}
|
||||||
|
|
||||||
|
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.1")
|
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
|
||||||
implementation("androidx.activity:activity-compose:1.7.0")
|
|
||||||
|
//UI
|
||||||
|
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("io.coil-kt:coil-compose:2.4.0")
|
||||||
implementation("androidx.compose.ui:ui")
|
implementation("androidx.compose.ui:ui")
|
||||||
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")
|
||||||
|
@ -10,6 +10,7 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import com.example.myapplication.composeui.navigation.MainNavbar
|
||||||
import com.example.myapplication.ui.theme.MyApplicationTheme
|
import com.example.myapplication.ui.theme.MyApplicationTheme
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
@ -19,7 +20,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
MyApplicationTheme {
|
MyApplicationTheme {
|
||||||
// A surface container using the 'background' color from the theme
|
// A surface container using the 'background' color from the theme
|
||||||
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
|
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
|
||||||
Greeting("World")
|
MainNavbar()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
package com.example.myapplication.category.composeui
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
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.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.example.myapplication.category.model.Category
|
||||||
|
import com.example.myapplication.database.AppDatabase
|
||||||
|
import com.example.myapplication.ui.theme.MyApplicationTheme
|
||||||
|
import com.example.myapplication.user.model.User
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
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(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(all = 10.dp)
|
||||||
|
.verticalScroll(rememberScrollState())) {
|
||||||
|
categories.forEachIndexed() { _, category ->
|
||||||
|
Row(Modifier.padding(all = 20.dp)) {
|
||||||
|
Text(category.category_name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||||
|
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
fun CategoryPreview() {
|
||||||
|
MyApplicationTheme {
|
||||||
|
Surface(
|
||||||
|
color = MaterialTheme.colorScheme.background
|
||||||
|
) {
|
||||||
|
CategoriesList(navController = null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.example.myapplication.category.model
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(tableName = "categories")
|
||||||
|
data class Category(
|
||||||
|
@PrimaryKey val uid: Int,
|
||||||
|
val category_name: String,
|
||||||
|
)
|
||||||
|
|
@ -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)
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
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.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(
|
||||||
|
@PrimaryKey val challenge_id: Int,
|
||||||
|
val challenge_name: String,
|
||||||
|
val challenge_status: Boolean,
|
||||||
|
val category_id: Int,
|
||||||
|
@ColumnInfo(index = true)
|
||||||
|
val user_id: Int,
|
||||||
|
)
|
@ -0,0 +1,46 @@
|
|||||||
|
package com.example.myapplication.composeui
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
import com.example.myapplication.ui.theme.MyApplicationTheme
|
||||||
|
import com.example.myapplication.R;
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun About() {
|
||||||
|
val localContext = LocalContext.current
|
||||||
|
val aboutText = localContext.resources.getText(R.string.about_text)
|
||||||
|
|
||||||
|
Column(Modifier.padding(all = 10.dp)) {
|
||||||
|
AndroidView(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth(),
|
||||||
|
factory = { context -> TextView(context) },
|
||||||
|
update = { it.text = aboutText }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||||
|
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
fun AboutPreview() {
|
||||||
|
MyApplicationTheme {
|
||||||
|
Surface(
|
||||||
|
color = MaterialTheme.colorScheme.background
|
||||||
|
) {
|
||||||
|
About()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,154 @@
|
|||||||
|
package com.example.myapplication.composeui.navigation
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.NavigationBar
|
||||||
|
import androidx.compose.material3.NavigationBarItem
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.navigation.NavDestination
|
||||||
|
import androidx.navigation.NavDestination.Companion.hierarchy
|
||||||
|
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||||
|
import androidx.navigation.NavHostController
|
||||||
|
import androidx.navigation.NavType
|
||||||
|
import androidx.navigation.compose.NavHost
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import androidx.navigation.navArgument
|
||||||
|
import com.example.myapplication.R
|
||||||
|
import com.example.myapplication.category.composeui.CategoriesList
|
||||||
|
import com.example.myapplication.composeui.About
|
||||||
|
import com.example.myapplication.ui.theme.MyApplicationTheme
|
||||||
|
import com.example.myapplication.user.composeui.UserView
|
||||||
|
import com.example.myapplication.user.composeui.UsersList
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun Topbar(
|
||||||
|
navController: NavHostController,
|
||||||
|
currentScreen: Screen?
|
||||||
|
) {
|
||||||
|
TopAppBar(
|
||||||
|
colors = TopAppBarDefaults.smallTopAppBarColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
|
titleContentColor = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
),
|
||||||
|
title = {
|
||||||
|
Text(stringResource(currentScreen?.resourceId ?: R.string.app_name))
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
if (
|
||||||
|
navController.previousBackStackEntry != null
|
||||||
|
&& (currentScreen == null || !currentScreen.showInBottomBar)
|
||||||
|
) {
|
||||||
|
IconButton(onClick = { navController.navigateUp() }) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.ArrowBack,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.onPrimary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Navbar(
|
||||||
|
navController: NavHostController,
|
||||||
|
currentDestination: NavDestination?,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
NavigationBar(modifier) {
|
||||||
|
Screen.bottomBarItems.forEach { screen ->
|
||||||
|
NavigationBarItem(
|
||||||
|
icon = { Icon(screen.icon, contentDescription = null) },
|
||||||
|
label = { Text(stringResource(screen.resourceId)) },
|
||||||
|
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
|
||||||
|
onClick = {
|
||||||
|
navController.navigate(screen.route) {
|
||||||
|
popUpTo(navController.graph.findStartDestination().id) {
|
||||||
|
saveState = true
|
||||||
|
}
|
||||||
|
launchSingleTop = true
|
||||||
|
restoreState = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Navhost(
|
||||||
|
navController: NavHostController,
|
||||||
|
innerPadding: PaddingValues, modifier:
|
||||||
|
Modifier = Modifier
|
||||||
|
) {
|
||||||
|
NavHost(
|
||||||
|
navController,
|
||||||
|
startDestination = Screen.About.route,
|
||||||
|
modifier.padding(innerPadding)
|
||||||
|
) {
|
||||||
|
composable(Screen.CategoryList.route) { CategoriesList(navController)}
|
||||||
|
composable(Screen.About.route) { About() }
|
||||||
|
composable(Screen.UserList.route) { UsersList(navController)}
|
||||||
|
composable(
|
||||||
|
Screen.UserView.route,
|
||||||
|
arguments = listOf(navArgument("id") { type = NavType.IntType })
|
||||||
|
) { backStackEntry ->
|
||||||
|
backStackEntry.arguments?.let { UserView(it.getInt("id")) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun MainNavbar() {
|
||||||
|
val navController = rememberNavController()
|
||||||
|
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||||
|
val currentDestination = navBackStackEntry?.destination
|
||||||
|
val currentScreen = currentDestination?.route?.let { Screen.getItem(it) }
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
Topbar(navController, currentScreen)
|
||||||
|
},
|
||||||
|
bottomBar = {
|
||||||
|
if (currentScreen == null || currentScreen.showInBottomBar) {
|
||||||
|
Navbar(navController, currentDestination)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) { innerPadding ->
|
||||||
|
Navhost(navController, innerPadding)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||||
|
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
fun MainNavbarPreview() {
|
||||||
|
MyApplicationTheme {
|
||||||
|
Surface(
|
||||||
|
color = MaterialTheme.colorScheme.background
|
||||||
|
) {
|
||||||
|
MainNavbar()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
package com.example.myapplication.composeui.navigation
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Favorite
|
||||||
|
import androidx.compose.material.icons.filled.Info
|
||||||
|
import androidx.compose.material.icons.filled.List
|
||||||
|
import androidx.compose.material.icons.filled.Person
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import com.example.myapplication.R
|
||||||
|
|
||||||
|
enum class Screen(
|
||||||
|
val route: String,
|
||||||
|
@StringRes val resourceId: Int,
|
||||||
|
val icon: ImageVector = Icons.Filled.Favorite,
|
||||||
|
val showInBottomBar: Boolean = true
|
||||||
|
) {
|
||||||
|
CategoryList(
|
||||||
|
"categories-list", R.string.categories_list, Icons.Filled.List
|
||||||
|
),
|
||||||
|
UserList(
|
||||||
|
"users-list", R.string.user_list, Icons.Filled.Person
|
||||||
|
),
|
||||||
|
About(
|
||||||
|
"about", R.string.about_main_title, Icons.Filled.Info
|
||||||
|
),
|
||||||
|
UserView(
|
||||||
|
"user-view/{id}", R.string.user_view_title, showInBottomBar = false
|
||||||
|
);
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val bottomBarItems = listOf(
|
||||||
|
CategoryList,
|
||||||
|
UserList,
|
||||||
|
About,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getItem(route: String): Screen? {
|
||||||
|
val findRoute = route.split("/").first()
|
||||||
|
return values().find { value -> value.route.startsWith(findRoute) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
package com.example.myapplication.user.composeui
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
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.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.example.myapplication.ui.theme.MyApplicationTheme
|
||||||
|
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)
|
||||||
|
@Composable
|
||||||
|
fun UserView(id: Int) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val (userWithChallanges, setUserWithChallanges) = remember { mutableStateOf<UserWithChallenges?>(null) }
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
setUserWithChallanges(AppDatabase.getInstance(context).userDao().getByUid(id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(all = 10.dp)
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
) {
|
||||||
|
OutlinedTextField(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = userWithChallanges?.user?.login.toString(),
|
||||||
|
onValueChange = {},
|
||||||
|
readOnly = true,
|
||||||
|
label = {
|
||||||
|
Text(stringResource(id = R.string.user_login))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
OutlinedTextField(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
value = userWithChallanges?.user?.fio.toString(),
|
||||||
|
onValueChange = {},
|
||||||
|
readOnly = true,
|
||||||
|
label = {
|
||||||
|
Text(stringResource(id = R.string.user_fio))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
userWithChallanges?.challenges?.forEachIndexed() { _, challenge ->
|
||||||
|
Row {
|
||||||
|
Text(text = challenge.challenge_name)
|
||||||
|
if (challenge.challenge_status) {
|
||||||
|
Text(text = " - Выполнено")
|
||||||
|
} else {
|
||||||
|
Text(text = " - В процессе")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||||
|
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
fun PerformanceViewPreview() {
|
||||||
|
MyApplicationTheme {
|
||||||
|
Surface(
|
||||||
|
color = MaterialTheme.colorScheme.background
|
||||||
|
) {
|
||||||
|
UserView(id = 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
package com.example.myapplication.user.composeui
|
||||||
|
|
||||||
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
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.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.example.myapplication.composeui.navigation.Screen
|
||||||
|
import com.example.myapplication.database.AppDatabase
|
||||||
|
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
|
||||||
|
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(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(all = 10.dp)
|
||||||
|
.verticalScroll(rememberScrollState())) {
|
||||||
|
users.forEachIndexed() { _, user ->
|
||||||
|
val userId = Screen.UserView.route.replace("{id}", (user.uid).toString())
|
||||||
|
Row(Modifier.padding(all = 10.dp)) {
|
||||||
|
Button(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(all = 10.dp),
|
||||||
|
onClick = { navController?.navigate(userId) }) {
|
||||||
|
Text(user.fio)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
|
||||||
|
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||||
|
@Composable
|
||||||
|
fun UserPreview() {
|
||||||
|
MyApplicationTheme {
|
||||||
|
Surface(
|
||||||
|
color = MaterialTheme.colorScheme.background
|
||||||
|
) {
|
||||||
|
UsersList(navController = null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.example.myapplication.user.model
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import com.example.myapplication.challenge.model.Challenge
|
||||||
|
|
||||||
|
@Entity(tableName = "users")
|
||||||
|
data class User(
|
||||||
|
@PrimaryKey val uid: Int,
|
||||||
|
val login: String,
|
||||||
|
val password: String,
|
||||||
|
val fio: String,
|
||||||
|
)
|
@ -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>
|
||||||
|
)
|
@ -1,3 +1,18 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">My Application</string>
|
<string name="app_name">My Application</string>
|
||||||
|
<string name="about_main_title">О нас</string>
|
||||||
|
<string name="user_list">Пользователи</string>
|
||||||
|
<string name="categories_list">Категории</string>
|
||||||
|
<string name="about_text">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||||
|
eiusmod tempor incididunt ut labore et dolore magna aliqua. Volutpat odio facilisis
|
||||||
|
mauris sit amet massa vitae. Arcu dui vivamus arcu felis bibendum ut tristique.
|
||||||
|
Leo in vitae turpis massa sed elementum. Tristique sollicitudin nibh sit amet commodo.
|
||||||
|
Donec ultrices tincidunt arcu non sodales neque sodales. Convallis aenean et tortor at.
|
||||||
|
Nisi est sit amet facilisis magna etiam tempor orci. Dignissim diam quis enim lobortis.
|
||||||
|
Lacus laoreet non curabitur gravida. Netus et malesuada fames ac turpis egestas maecenas.
|
||||||
|
Ornare aenean euismod elementum nisi quis eleifend. Elit sed vulputate mi sit. Sit amet
|
||||||
|
justo donec enim diam vulputate ut. Laoreet non curabitur gravida arcu.</string>
|
||||||
|
<string name="user_login">Логин</string>
|
||||||
|
<string name="user_fio">ФИО</string>
|
||||||
|
<string name="user_view_title">Пользователь</string>
|
||||||
</resources>
|
</resources>
|
@ -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
|
||||||
}
|
}
|
BIN
Лаб4_Отчет.docx
Normal file
BIN
Лаб4_Отчет.docx
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user