Загрузка данных


Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.SignUpLoginInv2">
        <activity
            android:name=".LoginIn"
            android:exported="false"
            android:label="@string/app_name"
            android:theme="@style/Theme.SignUpLoginInv2" />
        <activity
            android:name=".SignUp"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.SignUpLoginInv2">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
Login:
package com.example.signuplogininv2

import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

class LoginIn : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()

        val email = intent.getStringExtra(EXTRA_EMAIL).orEmpty()
        val password = intent.getStringExtra(EXTRA_PASSWORD).orEmpty()

        setContent {
            LoginScreen(
                initialEmail = email,
                initialPassword = password,
                onOpenSignUp = {
                    startActivity(Intent(this, SignUp::class.java))
                }
            )
        }
    }

    companion object {
        const val EXTRA_EMAIL = "extra_email"
        const val EXTRA_PASSWORD = "extra_password"
    }
}

@Composable
fun LoginScreen(
    initialEmail: String = "",
    initialPassword: String = "",
    onOpenSignUp: () -> Unit
) {
    var email by rememberSaveable { mutableStateOf(initialEmail) }
    var password by rememberSaveable { mutableStateOf(initialPassword) }
    var passwordVisible by rememberSaveable { mutableStateOf(false) }

    var emailError by rememberSaveable { mutableStateOf<String?>(null) }
    var passwordError by rememberSaveable { mutableStateOf<String?>(null) }

    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.White)
    ) {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(horizontal = 24.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Spacer(modifier = Modifier.height(88.dp))

            Text(
                text = "Привет!",
                fontSize = 32.sp,
                fontWeight = FontWeight.Bold,
                color = Color(0xFF2B2B2B)
            )

            Spacer(modifier = Modifier.height(8.dp))

            Text(
                text = "Заполните свои данные или\nпродолжите через социальные медиа",
                fontSize = 16.sp,
                color = Color(0xFF707B81),
                textAlign = TextAlign.Center
            )

            Spacer(modifier = Modifier.height(22.dp))

            AuthTextField(
                label = "Email",
                value = email,
                onValueChange = {
                    email = it
                    emailError = null
                },
                placeholder = "**********@gmail.com",
                keyboardType = KeyboardType.Email,
                errorText = emailError
            )

            Spacer(modifier = Modifier.height(12.dp))

            AuthTextField(
                label = "Пароль",
                value = password,
                onValueChange = {
                    password = it
                    passwordError = null
                },
                placeholder = "********",
                keyboardType = KeyboardType.Password,
                isPassword = true,
                passwordVisible = passwordVisible,
                onTogglePasswordVisibility = { passwordVisible = !passwordVisible },
                errorText = passwordError
            )

            Spacer(modifier = Modifier.weight(1f))

            Button(
                onClick = {
                    val trimmedEmail = email.trim()
                    emailError = validateLoginEmail(trimmedEmail)
                    passwordError = validateLoginPassword(password)
                },
                modifier = Modifier
                    .fillMaxWidth()
                    .height(46.dp),
                shape = RoundedCornerShape(13.dp),
                colors = ButtonDefaults.buttonColors(
                    containerColor = Color(0xFF48B2E7)
                )
            ) {
                Text(
                    text = "Войти",
                    fontSize = 14.sp
                )
            }

            Spacer(modifier = Modifier.height(24.dp))

            Row(
                horizontalArrangement = Arrangement.Center,
                verticalAlignment = Alignment.CenterVertically,
                modifier = Modifier
                    .padding(bottom = 24.dp)
                    .clickable { onOpenSignUp() }
            ) {
                Text(
                    text = "Вы впервые? ",
                    fontSize = 16.sp,
                    color = Color(0xFF6A6A6A)
                )

                Text(
                    text = "Создать пользователя",
                    fontSize = 16.sp,
                    color = Color(0xFF2B2B2B)
                )
            }
        }
    }
}

private fun validateLoginEmail(email: String): String? = when {
    email.isBlank() -> "Введите email."
    !email.contains("@") -> "Email должен содержать символ @."
    else -> null
}

private fun validateLoginPassword(password: String): String? = when {
    password.isBlank() -> "Введите пароль."
    else -> null
}

@Preview(showSystemUi = true)
@Composable
private fun LoginScreenPreview() {
    LoginScreen(
        initialEmail = "username@gmail.com",
        initialPassword = "12345678",
        onOpenSignUp = {}
    )
}
Sign: 
package com.example.signuplogininv2

import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding

import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Visibility
import androidx.compose.material.icons.filled.VisibilityOff
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Checkbox
import androidx.compose.material3.CheckboxDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

class SignUp : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            RegistrationScreen(
                onOpenLogin = { email, password ->
                    startActivity(Intent(this, LoginIn::class.java).apply {
                        putExtra(LoginIn.EXTRA_EMAIL, email)
                        putExtra(LoginIn.EXTRA_PASSWORD, password)
                    })
                }
            )
        }
    }
}

@Composable
fun RegistrationScreen(
    onOpenLogin: (String, String) -> Unit
) {
    var login by rememberSaveable { mutableStateOf("") }
    var email by rememberSaveable { mutableStateOf("") }
    var password by rememberSaveable { mutableStateOf("") }
    var confirmPassword by rememberSaveable { mutableStateOf("") }
    var isPrivacyPolicyChecked by rememberSaveable { mutableStateOf(false) }

    var passwordVisible by rememberSaveable { mutableStateOf(false) }
    var confirmPasswordVisible by rememberSaveable { mutableStateOf(false) }

    var loginError by rememberSaveable { mutableStateOf<String?>(null) }
    var emailError by rememberSaveable { mutableStateOf<String?>(null) }
    var passwordError by rememberSaveable { mutableStateOf<String?>(null) }
    var confirmPasswordError by rememberSaveable { mutableStateOf<String?>(null) }
    var agreementError by rememberSaveable { mutableStateOf<String?>(null) }

    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.White)
    ) {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(horizontal = 24.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Spacer(modifier = Modifier.height(60.dp))

            Text(
                text = "Регистрация",
                fontSize = 32.sp,
                fontWeight = FontWeight.Bold,
                color = Color(0xFF2B2B2B)
            )

            Spacer(modifier = Modifier.height(8.dp))

            Text(
                text = "Заполните свои данные или\nпродолжите через социальные медиа",
                fontSize = 16.sp,
                color = Color(0xFF707B81),
                textAlign = TextAlign.Center
            )

            Spacer(modifier = Modifier.height(22.dp))

            AuthTextField(
                label = "Логин",
                value = login,
                onValueChange = {
                    login = it
                    loginError = null
                },
                placeholder = "loginperson",
                errorText = loginError
            )

            Spacer(modifier = Modifier.height(12.dp))

            AuthTextField(
                label = "Email",
                value = email,
                onValueChange = {
                    email = it
                    emailError = null
                },
                placeholder = "**********@gmail.com",
                keyboardType = KeyboardType.Email,
                errorText = emailError
            )

            Spacer(modifier = Modifier.height(12.dp))

            AuthTextField(
                label = "Пароль",
                value = password,
                onValueChange = {
                    password = it.filter(Char::isDigit)
                    passwordError = null
                },
                placeholder = "********",
                keyboardType = KeyboardType.NumberPassword,
                isPassword = true,
                passwordVisible = passwordVisible,
                onTogglePasswordVisibility = { passwordVisible = !passwordVisible },
                errorText = passwordError
            )

            Spacer(modifier = Modifier.height(12.dp))

            AuthTextField(
                label = "Повторите пароль",
                value = confirmPassword,
                onValueChange = {
                    confirmPassword = it.filter(Char::isDigit)
                    confirmPasswordError = null
                },
                placeholder = "********",
                keyboardType = KeyboardType.NumberPassword,
                isPassword = true,
                passwordVisible = confirmPasswordVisible,
                onTogglePasswordVisibility = { confirmPasswordVisible = !confirmPasswordVisible },
                errorText = confirmPasswordError
            )

            Spacer(modifier = Modifier.height(12.dp))

            Row(
                verticalAlignment = Alignment.Top,
                modifier = Modifier.fillMaxWidth()
            ) {
                Checkbox(
                    checked = isPrivacyPolicyChecked,
                    onCheckedChange = {
                        isPrivacyPolicyChecked = it
                        agreementError = null
                    },
                    colors = CheckboxDefaults.colors(
                        checkedColor = Color(0xFF0560FA),
                        uncheckedColor = Color(0xFF6A6A6A)
                    )
                )

                Column(modifier = Modifier.padding(top = 11.dp)) {
                    Text(
                        text = "Даю согласие на обработку\nперсональных данных",
                        fontSize = 16.sp,
                        color = Color(0xFF6A6A6A),
                        textDecoration = TextDecoration.Underline
                    )

                    agreementError?.let {
                        Text(
                            text = it,
                            color = Color(0xFFB3261E),
                            fontSize = 12.sp,
                            modifier = Modifier.padding(top = 4.dp)
                        )
                    }
                }
            }

            Spacer(modifier = Modifier.weight(1f))

            Button(
                onClick = {
                    val trimmedLogin = login.trim()
                    val trimmedEmail = email.trim()
                    val loginValidation = validateLogin(trimmedLogin)
                    val emailValidation = validateEmail(trimmedEmail)
                    val passwordValidation = validatePassword(password)
                    val confirmValidation = validateConfirmPassword(password, confirmPassword)
                    val agreementValidation = if (isPrivacyPolicyChecked) {
                        null
                    } else {
                        "Подтвердите согласие на обработку персональных данных."
                    }

                    loginError = loginValidation
                    emailError = emailValidation
                    passwordError = passwordValidation
                    confirmPasswordError = confirmValidation
                    agreementError = agreementValidation

                    val hasErrors = listOf(
                        loginValidation,
                        emailValidation,
                        passwordValidation,
                        confirmValidation,
                        agreementValidation
                    ).any { it != null }

                    if (!hasErrors) {
                        onOpenLogin(trimmedEmail, password)
                    }
                },
                modifier = Modifier
                    .fillMaxWidth()
                    .height(46.dp),
                shape = RoundedCornerShape(13.dp),
                colors = ButtonDefaults.buttonColors(
                    containerColor = Color(0xFF48B2E7)
                )
            ) {
                Text(
                    text = "Зарегистрироваться",
                    fontSize = 14.sp
                )
            }

            Spacer(modifier = Modifier.height(24.dp))

            Row(
                horizontalArrangement = Arrangement.Center,
                verticalAlignment = Alignment.CenterVertically,
                modifier = Modifier
                    .padding(bottom = 24.dp)
                    .clickable { onOpenLogin(email.trim(), password) }
            ) {
                Text(
                    text = "Есть аккаунт? ",
                    fontSize = 16.sp,
                    color = Color(0xFF6A6A6A)
                )

                Text(
                    text = "Войти",
                    fontSize = 16.sp,
                    color = Color(0xFF2B2B2B)
                )
            }
        }
    }
}

@Composable
fun AuthTextField(
    label: String,
    value: String,
    onValueChange: (String) -> Unit,
    placeholder: String,
    modifier: Modifier = Modifier,
    keyboardType: KeyboardType = KeyboardType.Text,
    isPassword: Boolean = false,
    passwordVisible: Boolean = false,
    onTogglePasswordVisibility: (() -> Unit)? = null,
    errorText: String? = null
) {
    Text(
        text = label,
        fontSize = 16.sp,
        color = Color(0xFF2B2B2B),
        modifier = modifier
            .fillMaxWidth()
            .padding(start = 4.dp)
    )

    OutlinedTextField(
        value = value,
        onValueChange = onValueChange,
        modifier = Modifier
            .fillMaxWidth()
            .padding(top = 12.dp),
        placeholder = { Text(placeholder, color = Color(0xFF6A6A6A)) },
        shape = RoundedCornerShape(14.dp),
        singleLine = true,
        textStyle = TextStyle(color = Color.Black, fontSize = 16.sp),
        visualTransformation = if (isPassword && !passwordVisible) {
            PasswordVisualTransformation()
        } else {
            VisualTransformation.None
        },
        keyboardOptions = KeyboardOptions(keyboardType = keyboardType),
        isError = errorText != null,
        colors = OutlinedTextFieldDefaults.colors(
            focusedTextColor = Color.Black,
            unfocusedTextColor = Color.Black,
            focusedBorderColor = Color(0xFF48B2E7),
            unfocusedBorderColor = Color(0xFF48B2E7),
            errorBorderColor = Color(0xFFB3261E),
            errorTextColor = Color.Black
        ),
        trailingIcon = if (isPassword && onTogglePasswordVisibility != null) {
            {
                IconButton(onClick = onTogglePasswordVisibility) {
                    Icon(
                        imageVector = if (passwordVisible) {
                            Icons.Default.Visibility
                        } else {
                            Icons.Default.VisibilityOff
                        },
                        contentDescription = if (passwordVisible) {
                            "Скрыть пароль"
                        } else {
                            "Показать пароль"
                        },
                        tint = Color.Gray
                    )
                }
            }
        } else {
            null
        },
        supportingText = {
            errorText?.let {
                Text(text = it, color = Color(0xFFB3261E))
            }
        }
    )
}

private fun validateLogin(login: String): String? = when {
    login.isBlank() -> "Введите логин."
    login.length < 8 -> "Логин должен содержать не менее 8 символов."
    else -> null
}

private fun validateEmail(email: String): String? = when {
    email.isBlank() -> "Введите email."
    email.length < 12 -> "Email должен содержать не менее 12 символов."
    !email.contains("@") -> "Email должен содержать символ @."
    else -> null
}

private fun validatePassword(password: String): String? = when {
    password.isBlank() -> "Введите пароль."
    password.length < 8 -> "Пароль должен содержать не менее 8 цифр."
    !password.all(Char::isDigit) -> "Пароль должен состоять только из цифр."
    else -> null
}

private fun validateConfirmPassword(password: String, confirmPassword: String): String? = when {
    confirmPassword.isBlank() -> "Повторите пароль."
    confirmPassword.length < 8 -> "Повторный пароль должен содержать не менее 8 цифр."
    password != confirmPassword -> "Пароли должны совпадать."
    else -> null
}

@Preview(showSystemUi = true)
@Composable
private fun RegistrationScreenPreview() {
    RegistrationScreen(onOpenLogin = { _, _ -> })
}