add any functional

This commit is contained in:
KP9lK 2024-12-19 07:14:02 +03:00
parent df8fa84464
commit 0bd5c0792b
20 changed files with 331 additions and 36 deletions

View File

@ -14,6 +14,9 @@
<SelectionState runConfigName="MainActivity (1)">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
<SelectionState runConfigName="MainActivity (2)">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
</selectionStates>
</component>
</project>

View File

@ -2,6 +2,7 @@ plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
kotlin("plugin.serialization") version "2.0.21"
}
android {
@ -45,6 +46,12 @@ dependencies {
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.retrofit)
implementation (libs.converter.kotlinx.serialization)
implementation(libs.kotlinx.serialization.json)
implementation (libs.androidx.navigation.compose)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
@ -10,6 +10,7 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:usesCleartextTraffic="true"
android:theme="@style/Theme.AppForKids"
tools:targetApi="31">
<activity

View File

@ -4,14 +4,11 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.example.appforkids.screen.main.ClickerScreen
import com.example.appforkids.ui.theme.AppForKidsTheme
class MainActivity : ComponentActivity() {
@ -19,8 +16,8 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
AppForKidsTheme {
ClickerScreen()
Surface {
Navigation()
}
}
}
@ -34,10 +31,11 @@ fun Greeting(name: String, modifier: Modifier = Modifier) {
)
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
AppForKidsTheme {
Greeting("Android")
}
}
}

View File

@ -0,0 +1,44 @@
package com.example.appforkids
import androidx.compose.runtime.Composable
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.toRoute
import com.example.appforkids.data.Models.Response.UserResponse
import com.example.appforkids.screen.authorize.AuthorizeScreen
import com.example.appforkids.screen.main.ClickerScreen
import com.example.appforkids.screen.reigistration.RegistrationScreen
import com.example.appforkids.screen.score.ScoreScreen
import kotlinx.serialization.Serializable
@Serializable
object Registration
@Serializable
object Authorization
@Serializable
object Score
@Serializable
data class Clicker(val userId: Int)
@Composable
fun Navigation() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Registration){
composable<Registration> { RegistrationScreen(onNavigateToAuthorization = {
navController.navigate(route = Authorization)
}) }
composable<Authorization> { AuthorizeScreen({ userId ->
navController.navigate(route = Clicker(userId = userId))
}) }
composable<Clicker>{ navBackStackEntry ->
val clicker: Clicker = navBackStackEntry.toRoute()
ClickerScreen(clicker.userId, {navController.navigate(route = Score)})
}
composable<Score> { ScoreScreen({navController.navigate(route = Clicker)}) }
}
}

View File

@ -0,0 +1,24 @@
package com.example.appforkids.data
import com.example.appforkids.data.Models.Requests.AuthorizeUserRequest
import com.example.appforkids.data.Models.Requests.RegisterUserRequest
import com.example.appforkids.data.Models.Response.UserResponse
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Path
interface AppForKidsApi {
@GET("/user")
suspend fun getUsers():List<UserResponse>
@PUT("/user/{user_id}/clicks")
suspend fun registerClick(@Path("user_id") idUser:Int)
@POST("/authorize/auth")
suspend fun authorizeUser(@Body authorizeUserRequest: AuthorizeUserRequest):UserResponse?
@POST("/authorize/register")
suspend fun registerUser(@Body registerUserRequest: RegisterUserRequest):UserResponse?
}

View File

@ -0,0 +1,6 @@
package com.example.appforkids.data.Models.Requests
import kotlinx.serialization.Serializable
@Serializable
data class AuthorizeUserRequest(val login:String, val password: String)

View File

@ -0,0 +1,6 @@
package com.example.appforkids.data.Models.Requests
import kotlinx.serialization.Serializable
@Serializable
data class RegisterUserRequest(val login:String, val password:String, var url:String?=null)

View File

@ -0,0 +1,11 @@
package com.example.appforkids.data.Models.Response
import kotlinx.serialization.Serializable
@Serializable
data class UserResponse(
val id:Int,
val nickname: String,
val password: String,
val clicks:Int,
var url:String? = null )

View File

@ -0,0 +1,19 @@
package com.example.appforkids.data
import kotlinx.serialization.json.Json
import okhttp3.MediaType
import retrofit2.Retrofit
import retrofit2.converter.kotlinx.serialization.asConverterFactory
object HttpClient {
const val URL = "http://192.168.0.108:8080"
private val retrofit: Retrofit by lazy {
Retrofit.Builder()
.baseUrl(URL)
.addConverterFactory(Json.asConverterFactory(MediaType.get("application/json; charset=UTF8")))
.build()
}
val retrofitInstance by lazy {
retrofit.create(AppForKidsApi::class.java)
}
}

View File

@ -7,6 +7,8 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -15,33 +17,43 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.appforkids.data.Models.Response.UserResponse
import com.example.appforkids.screen.common.CommonButton
import com.example.appforkids.screen.common.CommonTextFieldWithLabel
import kotlinx.coroutines.flow.asStateFlow
@Preview
@Composable
fun AuthorizeScreen() {
fun AuthorizeScreen(onNavigateToClickerScreen: (Int) -> Unit) {
val authorizeViewModel:AuthorizeViewModel = viewModel()
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxSize().padding(5.dp),
modifier = Modifier
.fillMaxSize()
.padding(5.dp),
) {
var login by remember { mutableStateOf("") }
if(authorizeViewModel.isSignIn) {
onNavigateToClickerScreen(authorizeViewModel.userId)
authorizeViewModel.isSignIn = false;
}
CommonTextFieldWithLabel(
value = login,
onValueChange = { login = it},
value = authorizeViewModel.login,
onValueChange = { authorizeViewModel.login = it},
label = "Введите никнейм"
)
var password by remember { mutableStateOf("") }
CommonTextFieldWithLabel(
value = password,
onValueChange = { password = it},
value = authorizeViewModel.password,
onValueChange = {authorizeViewModel.password = it},
label = "Введите пароль"
)
CommonButton(
text = "Зарегистрироваться",
onClick = {}
text = "Войти",
onClick = {
authorizeViewModel.authorize()
}
)
}
}

View File

@ -0,0 +1,31 @@
package com.example.appforkids.screen.authorize
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.viewModelFactory
import com.example.appforkids.data.HttpClient
import com.example.appforkids.data.Models.Requests.AuthorizeUserRequest
import com.example.appforkids.data.Models.Response.UserResponse
import kotlinx.coroutines.launch
class AuthorizeViewModel(): ViewModel() {
var login by mutableStateOf("")
var password by mutableStateOf("")
var isSignIn by mutableStateOf(false)
var userId by mutableIntStateOf(-1)
fun authorize(){
val authorizeUserRequest = AuthorizeUserRequest(login = login, password = password)
viewModelScope.launch {
var user = HttpClient.retrofitInstance.authorizeUser(authorizeUserRequest)
if (user != null) {
isSignIn = true
userId = user.id
}
}
}
}

View File

@ -12,13 +12,15 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@Preview
@Composable
fun CommonButton(onClick: () -> Unit, text: String){
Button(onClick = onClick,
modifier = Modifier
.fillMaxWidth()
.height(40.dp)) {
.padding(vertical = 10.dp)
.fillMaxWidth()
.height(40.dp)
) {
Text(text = text)
}
}

View File

@ -24,9 +24,11 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.example.appforkids.R
import com.example.appforkids.data.Models.Response.UserResponse
import com.example.appforkids.screen.common.CommonButton
@Composable
fun ClickerScreen(){
fun ClickerScreen(userId: Int, OnRouteToRating: () -> Unit){
var countClick by remember { mutableIntStateOf(0) }
Column(
horizontalAlignment = Alignment.CenterHorizontally,
@ -43,5 +45,6 @@ fun ClickerScreen(){
.border(border = BorderStroke(1.dp, Color.Gray))
)
Text(text = countClick.toString())
CommonButton(onClick = {OnRouteToRating()}, text = "Открыть рейтинг")
}
}

View File

@ -4,44 +4,51 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.appforkids.screen.common.CommonButton
import com.example.appforkids.screen.common.CommonTextFieldWithLabel
@Preview
@Composable
fun RegistrationScreen() {
fun RegistrationScreen(onNavigateToAuthorization: () -> Unit) {
val registrationViewModel: RegistrationViewModel = viewModel(factory = RegistrationViewModel.Factory)
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxSize().padding(5.dp),
) {
var login by remember { mutableStateOf("") }
val registerState by registrationViewModel.registrationState.collectAsState()
CommonTextFieldWithLabel(
value = login,
onValueChange = { login = it},
value = registerState.login,
onValueChange = { registrationViewModel.setLogin(it)},
label = "Введите никнейм"
)
var password by remember { mutableStateOf("") }
CommonTextFieldWithLabel(
value = password,
onValueChange = { password = it},
value = registerState.password,
onValueChange = {registrationViewModel.setPassword(it)},
label = "Введите пароль"
)
CommonButton(
text = "Войти",
onClick = {}
text = "Зарегестрироваться",
onClick = {
registrationViewModel.registerUser()
onNavigateToAuthorization()
}
)
CommonButton(
text = "Авторизоваться",
onClick = {
onNavigateToAuthorization()
}
)
}
}

View File

@ -0,0 +1,3 @@
package com.example.appforkids.screen.reigistration
data class RegistrationState(var login: String = "", var password: String = "")

View File

@ -0,0 +1,42 @@
package com.example.appforkids.screen.reigistration
import androidx.lifecycle.AbstractSavedStateViewModelFactory
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.example.appforkids.data.AppForKidsApi
import com.example.appforkids.data.HttpClient
import com.example.appforkids.data.Models.Requests.RegisterUserRequest
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
class RegistrationViewModel(val appForKidsApi: AppForKidsApi):ViewModel() {
val registrationState = MutableStateFlow(RegistrationState())
fun registerUser(){
val userRequest = RegisterUserRequest(login = registrationState.value.login, password = registrationState.value.password)
viewModelScope.launch {
appForKidsApi.registerUser(userRequest)
}
}
fun setLogin(login: String){
viewModelScope.launch {
registrationState.emit(registrationState.value.copy(login = login))
}
}
fun setPassword(password: String){
viewModelScope.launch {
registrationState.emit(registrationState.value.copy(password = password))
}
}
companion object {
val Factory:ViewModelProvider.Factory = viewModelFactory {
initializer {
RegistrationViewModel(HttpClient.retrofitInstance)
}
}
}
}

View File

@ -0,0 +1,40 @@
package com.example.appforkids.screen.score
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.appforkids.screen.common.CommonButton
import com.example.appforkids.screen.common.CommonTextFieldWithLabel
@Composable
fun ScoreScreen(onNavigateToClickerScreen: () -> Unit) {
val registrationViewModel: ScoreViewModel = viewModel()
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier.fillMaxSize().padding(5.dp),
) {
val registerState by registrationViewModel.registrationState.collectAsState()
registerState.forEach {
Text(text = "${it.nickname} ${it.clicks}")
}
CommonButton(
text = "Вернуться",
onClick = {
onNavigateToClickerScreen()
}
)
}
}

View File

@ -0,0 +1,24 @@
package com.example.appforkids.screen.score
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.example.appforkids.data.AppForKidsApi
import com.example.appforkids.data.HttpClient
import com.example.appforkids.data.Models.Requests.RegisterUserRequest
import com.example.appforkids.data.Models.Response.UserResponse
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import okhttp3.Response
class ScoreViewModel():ViewModel() {
val registrationState = MutableStateFlow<List<UserResponse>>(emptyList())
init {
viewModelScope.launch {
registrationState.emit(HttpClient.retrofitInstance.getUsers())
}
}
}

View File

@ -1,16 +1,25 @@
[versions]
agp = "8.7.2"
converterGson = "2.11.0"
converterKotlinxSerialization = "2.11.0"
kotlin = "2.0.0"
coreKtx = "1.10.1"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
kotlinxSerializationJson = "1.7.3"
lifecycleRuntimeKtx = "2.6.1"
activityCompose = "1.8.0"
composeBom = "2024.04.01"
navigationCompose = "2.8.5"
retrofit2KotlinxSerializationConverter = "1.0.0"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" }
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" }
converter-kotlinx-serialization = { module = "com.squareup.retrofit2:converter-kotlinx-serialization", version.ref = "converterKotlinxSerialization" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
@ -24,6 +33,9 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "converterGson" }
retrofit2-kotlinx-serialization-converter = { module = "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter", version.ref = "retrofit2KotlinxSerializationConverter" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }