Загрузка данных
package com.example.myapplication
import android.content.Context
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import java.text.SimpleDateFormat
import java.util.*
import androidx.core.content.edit
val Green = Color(0xFF4CAF50)
val Bg = Color(0xFFF2F2F2)
//////////////// MAIN //////////////////
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { App() }
}
}
//////////////// USER STORAGE //////////////////
object UserStorage {
private const val PREFS = "prefs"
private const val USER = "user"
private const val PASS = "pass"
fun save(context: Context, u: String, p: String) {
context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
.edit { putString(USER, u).putString(PASS, p) }
}
fun get(context: Context): Pair<String, String>? {
val sp = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
val u = sp.getString(USER, null)
val p = sp.getString(PASS, null)
return if (u != null && p != null) Pair(u, p) else null
}
fun check(context: Context, u: String, p: String): Boolean {
val saved = get(context)
return saved?.first == u && saved.second == p
}
}
//////////////// APP //////////////////
@Composable
fun App() {
val ctx = LocalContext.current
var isLogged by remember { mutableStateOf(UserStorage.get(ctx) != null) }
var screen by remember { mutableStateOf("login") }
var isDark by remember { mutableStateOf(false) }
var lang by remember { mutableStateOf("ru") }
MaterialTheme(
colorScheme = if (isDark) darkColorScheme(
primary = Green,
background = Color(0xFF121212),
surface = Color(0xFF1E1E1E),
onBackground = Color.White,
onSurface = Color.White
) else lightColorScheme(
primary = Green,
background = Bg,
surface = Color.White,
onBackground = Color.Black,
onSurface = Color.Black
)
) {
when (screen) {
"login" -> LoginScreen(
lang = lang,
onLogin = {
if (UserStorage.get(ctx) != null) {
isLogged = true
screen = "notes"
}
},
onGoRegister = { screen = "register" }
)
"register" -> RegisterScreen(
lang = lang,
onRegister = {
isLogged = true
screen = "notes"
},
onBack = { screen = "login" }
)
"notes" -> NotesScreen(
lang = lang,
onOpenSettings = { screen = "settings" }
)
"settings" -> SettingsScreen(
lang = lang,
dark = isDark,
onBack = { screen = "notes" },
onThemeChange = { isDark = it },
onLangChange = { lang = it },
onLogout = {
isLogged = false
screen = "login"
}
)
}
}
}
//////////////// LOGIN //////////////////
@Composable
fun LoginScreen(
lang: String,
onLogin: () -> Unit,
onGoRegister: () -> Unit
) {
val ctx = LocalContext.current
var u by remember { mutableStateOf("") }
var p by remember { mutableStateOf("") }
var err by remember { mutableStateOf("") }
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Column(
Modifier
.fillMaxSize()
.padding(24.dp),
verticalArrangement = Arrangement.Center
) {
Text(if (lang == "ru") "Вход" else "Login")
OutlinedTextField(
value = u,
onValueChange = { u = it },
label = { Text("Login") }
)
OutlinedTextField(
value = p,
onValueChange = { p = it },
label = { Text("Password") }
)
if (err.isNotEmpty()) {
Text(err, color = Color.Red)
}
Button(onClick = {
if (UserStorage.check(ctx, u, p)) {
onLogin()
} else {
err = if (lang == "ru") "Ошибка входа" else "Login error"
}
}) {
Text(if (lang == "ru") "Войти" else "Sign in")
}
TextButton(onClick = onGoRegister) {
Text(if (lang == "ru") "Регистрация" else "Sign up")
}
}
}
}
//////////////// REGISTER //////////////////
@Composable
fun RegisterScreen(
lang: String,
onRegister: () -> Unit,
onBack: () -> Unit
) {
val ctx = LocalContext.current
var u by remember { mutableStateOf("") }
var p by remember { mutableStateOf("") }
var p2 by remember { mutableStateOf("") }
var err by remember { mutableStateOf("") }
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Column(
Modifier.padding(16.dp)
) {}
Text(if (lang == "ru") "Регистрация" else "Register")
OutlinedTextField(u, { u = it }, label = { Text("Login") })
OutlinedTextField(p, { p = it }, label = { Text("Password") })
OutlinedTextField(p2, { p2 = it }, label = { Text("Repeat") })
if (err.isNotEmpty()) Text(err, color = Color.Red)
Button(onClick = {
when {
u.isBlank() -> err = "Empty login"
p != p2 -> err = "Passwords mismatch"
else -> {
UserStorage.save(ctx, u, p)
onRegister()
}
}
}) {
Text(if (lang == "ru") "Создать аккаунт" else "Create account")
}
TextButton(onClick = onBack) {
Text(if (lang == "ru") "Назад" else "Back")
}
}
}
//////////////// NOTES //////////////////
data class Note(val title: String, val text: String, val date: String)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NotesScreen(lang: String, onOpenSettings: () -> Unit) {
val ctx = LocalContext.current
var notes by remember {
mutableStateOf(NotesStorage.load(ctx))
}
var showAdd by remember { mutableStateOf(false) }
var editIndex by remember { mutableStateOf<Int?>(null) }
Scaffold(
containerColor = MaterialTheme.colorScheme.background,
topBar = {
TopAppBar(
title = { Text(if (lang == "ru") "Quick Notes" else "Quick Notes") },
actions = {
IconButton(onClick = onOpenSettings) {
Icon(Icons.Default.Settings, null)
}
}
)
},
floatingActionButton = {
FloatingActionButton(onClick = { showAdd = true }) {
Icon(Icons.Default.Add, null)
}
}
) { padding ->
LazyColumn(Modifier.padding(padding)) {
itemsIndexed(notes) { index, note ->
Card(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth()
.clickable { editIndex = index },
shape = RoundedCornerShape(16.dp)
) {
Column(Modifier.padding(16.dp)) {
Text(note.title)
Text(note.text)
Spacer(Modifier.height(6.dp))
Text(
note.date,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
)
Spacer(Modifier.height(8.dp))
IconButton(onClick = {
notes = notes.toMutableList().also { it.removeAt(index) }
NotesStorage.save(ctx, notes)
}) {
Icon(Icons.Default.Delete, null)
}
}
}
}
}
}
// ➕ ADD NOTE
if (showAdd) {
var title by remember { mutableStateOf("") }
var text by remember { mutableStateOf("") }
AlertDialog(
onDismissRequest = { showAdd = false },
confirmButton = {
Button(onClick = {
val date = SimpleDateFormat(
"dd.MM.yyyy HH:mm",
Locale.getDefault()
).format(Date())
notes = notes + Note(title, text, date)
NotesStorage.save(ctx, notes)
showAdd = false
}) {
Text(if (lang == "ru") "Добавить" else "Add")
}
},
dismissButton = {
TextButton(onClick = { showAdd = false }) {
Text(if (lang == "ru") "Отмена" else "Cancel")
}
},
title = {
Text(if (lang == "ru") "Новая заметка" else "New note")
},
text = {
Column {
OutlinedTextField(
value = title,
onValueChange = { title = it },
label = { Text(if (lang == "ru") "Заголовок" else "Title") }
)
Spacer(Modifier.height(8.dp))
OutlinedTextField(
value = text,
onValueChange = { text = it },
label = { Text(if (lang == "ru") "Текст" else "Text") }
)
}
}
)
}
// ✏️ EDIT NOTE
editIndex?.let { index ->
var title by remember { mutableStateOf(notes[index].title) }
var text by remember { mutableStateOf(notes[index].text) }
AlertDialog(onDismissRequest = { editIndex = null },
confirmButton = {
Button(onClick = {
val updated = notes.toMutableList()
updated[index] = updated[index].copy(title = title, text = text)
notes = updated
NotesStorage.save(ctx, notes)
editIndex = null
}) {
Text(if (lang == "ru") "Сохранить" else "Save")
}
},
dismissButton = {
TextButton(onClick = { editIndex = null }) {
Text(if (lang == "ru") "Отмена" else "Cancel")
}
},
title = {
Text(if (lang == "ru") "Редактирование" else "Edit")
},
text = {
Column {
OutlinedTextField(
value = title,
onValueChange = { title = it },
label = { Text(if (lang == "ru") "Заголовок" else "Title") }
)
Spacer(Modifier.height(8.dp))
OutlinedTextField(
value = text,
onValueChange = { text = it },
label = { Text(if (lang == "ru") "Текст" else "Text") }
)
}
}
)
}
}
//////////////// SETTINGS //////////////////
@Composable
fun SettingsScreen(
lang: String,
dark: Boolean,
onBack: () -> Unit,
onThemeChange: (Boolean) -> Unit,
onLangChange: (String) -> Unit,
onLogout: () -> Unit
) {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Column(Modifier.padding(16.dp)) {
IconButton(onClick = onBack) {
Icon(Icons.Default.ArrowBack, null)
}
Row(verticalAlignment = Alignment.CenterVertically) {
Text(if (lang == "ru") "Тёмная тема" else "Dark theme")
Spacer(Modifier.weight(1f))
Switch(checked = dark, onCheckedChange = onThemeChange)
}
Spacer(Modifier.height(16.dp))
Row {
Text("RU", Modifier.clickable { onLangChange("ru") })
Spacer(Modifier.width(16.dp))
Text("EN", Modifier.clickable { onLangChange("en") })
}
Spacer(Modifier.height(20.dp))
Button(
onClick = onLogout,
colors = ButtonDefaults.buttonColors(containerColor = Color.Red)
) {
Text(if (lang == "ru") "Выйти" else "Logout")
}
}
}
}
//////////////// PREVIEW //////////////////