Compare commits

...

8 Commits

Author SHA1 Message Date
315e7e4afe
fixup! fix: apply API changes 2025-04-23 17:30:34 +03:00
55ff6e6cd1
impr: LoginActivity.kt
- Style changed
- Added loading indicator
- Added login/password validation
2025-04-23 16:38:29 +03:00
5e15a75664
feat: add marked divider 2025-04-23 14:52:06 +03:00
d7a2a26097
style: simplify db declaration 2025-04-23 00:36:20 +03:00
f1b330c1ce
fix: builds variants names 2025-04-22 23:24:44 +03:00
1ede1a4c2d
fix: apply warning fixes 2025-04-22 20:28:36 +03:00
08949c5f0d
impr: add empty list message 2025-04-22 18:44:04 +03:00
be230e8a11
fix: apply API changes 2025-04-22 18:27:44 +03:00
12 changed files with 254 additions and 189 deletions

View File

@ -34,16 +34,17 @@ android {
release {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
resValue("string", "app_name", "@string/app_name_reg")
}
debug {
applicationIdSuffix = ".debug"
versionNameSuffix =
"-debug+${LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)}"
resValue("string", "app_name", "app_name_dev")
"-debug+${LocalDateTime.now().format(DateTimeFormatter.BASIC_ISO_DATE)}"
resValue("string", "app_name", "@string/app_name_dev")
}
create("beta") {
versionNameSuffix =
"-beta+${LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)}"
"-beta+${LocalDateTime.now().format(DateTimeFormatter.BASIC_ISO_DATE)}"
resValue("string", "app_name", "@string/app_name_beta")
signingConfig = signingConfigs.getByName("debug")
}
@ -104,7 +105,8 @@ dependencies {
implementation(libs.coil.compose)
implementation(libs.androidx.datastore.preferences)
implementation(libs.splitties.funpack.android.base.with.views.dsl)
implementation(libs.splitties.base)
implementation(libs.splitties.room)
implementation(libs.compose)

View File

@ -19,11 +19,14 @@ import ru.sweetbread.unn.db.loadSchedule
import ru.sweetbread.unn.db.loadUserByBitrixId
import ru.sweetbread.unn.ui.layout.LoginData
import ru.sweetbread.unn.ui.layout.client
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
private lateinit var PHPSESSID: String
private lateinit var CSRF: String
lateinit var ME: User
@ -31,8 +34,10 @@ lateinit var ME: User
const val portalURL = "https://portal.unn.ru"
const val ruzapiURL = "$portalURL/ruzapi"
const val vuzapiURL = "$portalURL/bitrix/vuz/api"
const val prtl2URL = "$portalURL/portal2/api"
const val restURL = "$portalURL/rest"
enum class Type(val s: String) {
Student("student"),
Group("group"),
@ -109,7 +114,6 @@ class User(
class Post(
val id: Int,
val blogId: Int,
val authorId: Int,
val enableComments: Boolean,
val numComments: Int,
@ -123,6 +127,7 @@ class AvatarSet(
val small: String
)
/**
* Authorize user by [login] and [password]
*
@ -149,10 +154,8 @@ suspend fun auth(
if (r.status.value == 302) {
PHPSESSID =
"""PHPSESSID=([\w\d]+)""".toRegex().find(r.headers["Set-Cookie"]!!)!!.groupValues[1]
GlobalScope.launch(Dispatchers.IO) {
getMyself(login)
getCSRF()
}
return true
}
return false
@ -162,42 +165,44 @@ suspend fun auth(
* Save info about current [User] in memory
*/
private suspend fun getMyself(login: String) {
GlobalScope.launch(Dispatchers.IO) {
val studentinfo = JSONObject(client.get("$ruzapiURL/studentinfo") {
parameter("uns", login.substring(1))
}.bodyAsText())
// WARNING: trailing / is important, 'cuz API devs are eating shit
val studentinfo = JSONObject(client.get("$ruzapiURL/studentinfo/") {
header("Cookie", "PHPSESSID=$PHPSESSID")
parameter("uns", login.drop(1))
}.bodyAsText())
val user = JSONObject(
client.get("$vuzapiURL/user") {
header("Cookie", "PHPSESSID=$PHPSESSID")
}.bodyAsText()
)
val user = JSONObject(
client.get("$vuzapiURL/user") {
header("Cookie", "PHPSESSID=$PHPSESSID")
}.bodyAsText()
)
ME = User(
unnId = studentinfo.getString("id").toInt(),
bitrixId = user.getInt("bitrix_id"),
userId = user.getInt("id"),
type = when (studentinfo.getString("type")) {
"lecturer" -> Type.Lecturer // ig,,,
else -> Type.Student
},
email = user.getString("email"),
nameRu = user.getString("fullname"),
nameEn = user.getString("fullname_en"),
isMale = user.getString("sex") == "M",
birthday = LocalDate.parse(
user.getString("birthdate"),
DateTimeFormatter.ofPattern("yyyy-MM-dd")
),
avatar = user.getJSONObject("photo").let {
AvatarSet(
it.getString("orig"),
it.getString("thumbnail"),
it.getString("small"),
)
}
)
}
Log.d("studentInfo", studentinfo.toString(2))
ME = User(
unnId = studentinfo.getString("id").toInt(),
bitrixId = user.getInt("bitrix_id"),
userId = user.getInt("id"),
type = when (studentinfo.getString("type")) {
"lecturer" -> Type.Lecturer // ig,,,
else -> Type.Student
},
email = user.getString("email"),
nameRu = user.getString("fullname"),
nameEn = user.getString("fullname_en"),
isMale = user.getString("sex") == "M",
birthday = Instant
.parse(user.getString("birthdate"))
.atZone(ZoneId.of("Europe/Moscow"))
.toLocalDate(),
avatar = user.getJSONObject("photo").let {
AvatarSet(
it.getString("orig"),
it.getString("thumbnail"),
it.getString("small"),
)
}
)
}
suspend fun getScheduleDay(
@ -209,9 +214,8 @@ suspend fun getScheduleDay(
if ((type == ME.type) and (id == ME.unnId!!)) {
val schedule = withContext(Dispatchers.IO) { loadSchedule(date) }
Log.d("Schedule", schedule.joinToString())
if (schedule.size != 0) {
if (schedule.isNotEmpty())
return schedule
}
}
return getSchedule(type, id, date, date)
@ -223,7 +227,7 @@ suspend fun getSchedule(
start: LocalDate,
finish: LocalDate
): ArrayList<ScheduleUnit> {
val unnDatePattern = DateTimeFormatter.ofPattern("yyyy.MM.dd")
val unnDatePattern = DateTimeFormatter.ofPattern("yyyy-MM-dd")
val r = client.get("$ruzapiURL/schedule/${type.s}/$id") {
parameter("start", start.format(unnDatePattern))
@ -310,28 +314,26 @@ suspend fun getCSRF() {
}
suspend fun getBlogposts(): ArrayList<Post> {
val r = client.get("$restURL/log.blogpost.get") {
val r = client.get("$prtl2URL/news.php") {
header("Cookie", "PHPSESSID=$PHPSESSID")
parameter("sessid", CSRF)
header("x-bitrix-sessid-token", CSRF)
}
val json = JSONObject(r.bodyAsText())
val result = json.getJSONArray("result")
val result = JSONArray(r.bodyAsText())
val out = arrayListOf<Post>()
for (i in 0 until result.length()) {
val el = result.getJSONObject(i)
out.add(
Post(
id = el.getString("ID").toInt(),
blogId = el.getString("BLOG_ID").toInt(),
authorId = el.getString("AUTHOR_ID").toInt(),
enableComments = el.getString("ENABLE_COMMENTS") == "Y",
numComments = el.getString("NUM_COMMENTS").toInt(),
id = el.getString("id").toInt(),
authorId = el.getJSONObject("author").getInt("id").toInt(),
enableComments = true, // FIXME: Delete the field or get correct value
numComments = el.getString("commentsnum").toInt(),
date = LocalDateTime.parse(
el.getString("DATE_PUBLISH"),
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'+03:00'")
el.getString("time"),
DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss")
),
content = el.getString("DETAIL_TEXT")
content = el.getString("fulltext")
)
)
}
@ -375,10 +377,10 @@ suspend fun getUser(id: Int): User {
nameRu = json.getString("fullname"),
nameEn = json.getString("fullname_en"),
isMale = json.getString("sex") == "M",
birthday = LocalDate.parse(
json.getString("birthdate"),
DateTimeFormatter.ofPattern("yyyy-MM-dd")
),
birthday = Instant
.parse(json.getString("birthdate"))
.atZone(ZoneId.of("Europe/Moscow"))
.toLocalDate(),
avatar = json.getJSONObject("photo").let {
AvatarSet(
it.getString("orig"),

View File

@ -11,15 +11,15 @@ class UNNApp : Application() {
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
initAcra {
buildConfigClass = BuildConfig::class.java
reportFormat = StringFormat.JSON
httpSender {
uri = BuildConfig.ACRA_URL
basicAuthLogin = BuildConfig.ACRA_LOGIN
basicAuthPassword = BuildConfig.ACRA_PASS
httpMethod = HttpSender.Method.POST
}
}
// initAcra {
// buildConfigClass = BuildConfig::class.java
// reportFormat = StringFormat.JSON
// httpSender {
// uri = BuildConfig.ACRA_URL
// basicAuthLogin = BuildConfig.ACRA_LOGIN
// basicAuthPassword = BuildConfig.ACRA_PASS
// httpMethod = HttpSender.Method.POST
// }
// }
}
}

View File

@ -376,7 +376,7 @@ fun loadSchedule(oid: Int): ScheduleUnit? {
fun loadSchedule(date: LocalDate): ArrayList<ScheduleUnit> {
db.scheduleDao().getSchedule(date.format(DateTimeFormatter.ISO_DATE))
.mapNotNull { Log.d("meow", "${it.oid}: ${loadSchedule(it.oid)}") }
.map { Log.d("meow", "${it.oid}: ${loadSchedule(it.oid)}") }
return ArrayList(
db.scheduleDao().getSchedule(date.format(DateTimeFormatter.ISO_DATE))
.mapNotNull { loadSchedule(it.oid) }

View File

@ -1,3 +1,8 @@
/*
* Created by sweetbread
* Copyright (c) 2025. All rights reserved.
*/
package ru.sweetbread.unn.ui.composes
import android.text.util.Linkify
@ -47,7 +52,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import org.kefirsf.bb.BBProcessorFactory
import org.kefirsf.bb.TextProcessor
import ru.sweetbread.unn.AvatarSet
import ru.sweetbread.unn.Post
import ru.sweetbread.unn.R
@ -174,7 +178,7 @@ fun PostItem(modifier: Modifier = Modifier, post: Post, extended: Boolean = fals
LaunchedEffect(post) {
html = toHtml(processor, post)
html = post.content
user = getUserByBitrixId(post.authorId)
}
@ -218,45 +222,6 @@ fun PostItem(modifier: Modifier = Modifier, post: Post, extended: Boolean = fals
}
}
private fun toHtml(
processor: TextProcessor,
post: Post
): String {
Log.d("toHTML | original", post.content)
val result =
post.content.replace("""\[URL=(.+?)](.+?)\[/URL]""".toRegex(RegexOption.DOT_MATCHES_ALL)) {
"<a href=\"${it.groups[1]?.value}\">${it.groups[2]?.value}</a>"
}.replace("""\[FONT=(.+?)](.*?)\[/FONT]""".toRegex(RegexOption.DOT_MATCHES_ALL)) {
"<span style=\"font-family: ${it.groups[1]?.value}\">${it.groups[2]?.value}</span>"
}.replace("""\[SIZE=(.+?)](.*?)\[/SIZE]""".toRegex(RegexOption.DOT_MATCHES_ALL)) {
"<span style=\"font-size: ${it.groups[1]?.value}\">${it.groups[2]?.value}</span>"
}.replace(
"""\[CENTER]\[JUSTIFY]\[CENTER](.*?)\[/CENTER]\[/JUSTIFY]\[/CENTER]""".toRegex(
RegexOption.DOT_MATCHES_ALL
)
) {
"<span style=\"text-align: center;\">${it.groups[1]?.value}</span><br>"
}.replace("""\[CENTER](.*?)\[/CENTER]""".toRegex(RegexOption.DOT_MATCHES_ALL)) {
"<span style=\"text-align: center;\">${it.groups[1]?.value}</span><br>"
}.replace("""\[JUSTIFY](.*?)\[/JUSTIFY]""".toRegex(RegexOption.DOT_MATCHES_ALL)) {
"<span style=\"text-align: justify;\">${it.groups[1]?.value}</span><br>"
}.replace("""\[B](.*?)\[/B]""".toRegex(RegexOption.DOT_MATCHES_ALL)) {
"<b>${it.groups[1]?.value}</b>"
}.replace("""\[U](.*?)\[/U]""".toRegex(RegexOption.DOT_MATCHES_ALL)) {
"<u>${it.groups[1]?.value}</u>"
}.replace("""\[P](.*?)\[/P]""".toRegex(RegexOption.DOT_MATCHES_ALL)) {
"<p>${it.groups[1]?.value}</p>"
/*}.replace("""\[DISK FILE ID=n(\d+)]""".toRegex(RegexOption.DOT_MATCHES_ALL)) {
"<img src=\"$portalURL/bitrix/tools/disk/uf.php?attachedId=${it.groups[1]?.value}&action=download&ncc=1\" />"*/
}.replace("""\[IMG .+].+?\[/IMG]""".toRegex(RegexOption.DOT_MATCHES_ALL), "")
/*.replace("\n", "\n<br>")*/
Log.d("toHTML | result", result)
return result
}
@Preview
@Composable
@ -278,7 +243,6 @@ fun UserItemPreview() {
fun PostItemPreview() {
val post = Post(
id = 154923,
blogId = 121212,
authorId = 165945,
enableComments = true,
numComments = 0,

View File

@ -2,6 +2,7 @@ package ru.sweetbread.unn.ui.composes
import android.util.Log
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
@ -10,6 +11,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
@ -23,6 +25,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -31,15 +34,19 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import com.kizitonwose.calendar.compose.WeekCalendar
import com.kizitonwose.calendar.compose.weekcalendar.rememberWeekCalendarState
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import ru.sweetbread.unn.Auditorium
import ru.sweetbread.unn.Building
@ -51,11 +58,14 @@ import ru.sweetbread.unn.R
import ru.sweetbread.unn.ScheduleUnit
import ru.sweetbread.unn.getScheduleDay
import ru.sweetbread.unn.ui.theme.UNNTheme
import splitties.resources.appStr
import java.time.DayOfWeek
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.Calendar
@Composable
fun Schedule() {
@ -101,12 +111,15 @@ fun ScheduleDay(modifier: Modifier = Modifier, date: LocalDate) {
if (loadedDate == date) {
Log.d("Loaded", "${date.format(DateTimeFormatter.ISO_DATE)} ${lessons.size}")
LazyColumn (modifier) {
items(lessons) { // TODO: Add empty list notification
items(lessons) {
ScheduleItem(unit = it, modifier = Modifier.clickable {
expanded = if (it.oid == expanded) 0
else it.oid
}, expanded = expanded == it.oid)
}
if (lessons.isEmpty())
item { Text(appStr(R.string.noData)) }
}
} else {
LinearProgressIndicator(
@ -128,8 +141,37 @@ fun ScheduleDay(modifier: Modifier = Modifier, date: LocalDate) {
@Composable
fun ScheduleItem(modifier: Modifier = Modifier, unit: ScheduleUnit, expanded: Boolean = false) {
fun getRel(begin: Long, end: Long, now: Long): Float {
if ((begin > now) or (now > end))
return -1f
return (now - begin) / (end - begin).toFloat()
}
val begin = unit.begin.format(DateTimeFormatter.ofPattern("HH:mm"))
val end = unit.end.format(DateTimeFormatter.ofPattern("HH:mm"))
var rel by remember {
mutableFloatStateOf(getRel(
LocalDateTime.of(unit.date, unit.begin).atZone(ZoneId.of("Europe/Moscow")).toEpochSecond(),
LocalDateTime.of(unit.date, unit.end).atZone(ZoneId.of("Europe/Moscow")).toEpochSecond(),
LocalDateTime.now().atZone(ZoneId.of("Europe/Moscow")).toEpochSecond()
))
}
LaunchedEffect(Unit) {
while (true) {
val now = System.currentTimeMillis()
val calendar = Calendar.getInstance().apply { timeInMillis = now }
val seconds = calendar.get(Calendar.SECOND)
val millisUntilNextMinute = (60 - seconds) * 1000L - calendar.get(Calendar.MILLISECOND)
delay(millisUntilNextMinute)
rel = getRel(
LocalDateTime.of(unit.date, unit.begin).atZone(ZoneId.of("Europe/Moscow")).toEpochSecond(),
LocalDateTime.of(unit.date, unit.end).atZone(ZoneId.of("Europe/Moscow")).toEpochSecond(),
LocalDateTime.now().atZone(ZoneId.of("Europe/Moscow")).toEpochSecond()
)
}
}
Row (
modifier
@ -137,16 +179,9 @@ fun ScheduleItem(modifier: Modifier = Modifier, unit: ScheduleUnit, expanded: Bo
.padding(4.dp)
.clip(RoundedCornerShape(8.dp))
.background(
if ((LocalDateTime.of(
unit.date,
unit.begin
) < LocalDateTime.now()) and (LocalDateTime.now() < LocalDateTime.of(
unit.date,
unit.end
))
)
if (rel != -1f)
MaterialTheme.colorScheme.primaryContainer
else MaterialTheme.colorScheme.secondaryContainer
else MaterialTheme.colorScheme.surfaceContainer
)
.padding(8.dp)
){
@ -236,8 +271,18 @@ fun ScheduleItem(modifier: Modifier = Modifier, unit: ScheduleUnit, expanded: Bo
color = MaterialTheme.colorScheme.onBackground
)
Row (Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
Row (Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically) {
Text(begin.toString(), fontWeight = FontWeight.Bold)
if (rel != -1f)
DividerWithMarker(
Modifier.weight(1f).padding(horizontal = 2.dp),
positionPercentage = rel,
color = MaterialTheme.colorScheme.outline,
thickness = 3.dp,
markerSize = 8.dp,
markerColor = MaterialTheme.colorScheme.primary)
Text(end.toString())
}
}
@ -253,6 +298,43 @@ fun ScheduleItem(modifier: Modifier = Modifier, unit: ScheduleUnit, expanded: Bo
}
}
@Composable
fun DividerWithMarker(
modifier: Modifier = Modifier,
positionPercentage: Float, // от 0f до 1f (например, 0.5f = 50%)
color: Color = Color.Gray,
thickness: Dp = 1.dp,
markerSize: Dp = 8.dp,
markerColor: Color = Color.Red
) {
Canvas(modifier = modifier.height(thickness)) {
val dividerHeight = thickness.toPx()
val width = size.width
val markerX = width * positionPercentage
drawLine(
color = markerColor,
start = Offset(0f, dividerHeight / 2),
end = Offset(markerX, dividerHeight / 2),
strokeWidth = dividerHeight
)
drawLine(
color = color,
start = Offset(markerX, dividerHeight / 2),
end = Offset(width, dividerHeight / 2),
strokeWidth = dividerHeight / 2
)
drawCircle(
color = markerColor,
radius = markerSize.toPx() / 2,
center = Offset(markerX, dividerHeight / 2)
)
}
}
@Preview
@Composable
fun ScheduleItemPreview() {

View File

@ -1,3 +1,8 @@
/*
* Created by sweetbread
* Copyright (c) 2025. All rights reserved.
*/
package ru.sweetbread.unn.ui.layout
import android.os.Bundle
@ -7,19 +12,20 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -74,21 +80,26 @@ class LoginActivity : ComponentActivity() {
SnackbarHost(hostState = snackbarHostState)
}
) { innerPadding ->
LoginPanel(Modifier.padding(innerPadding), { login, password ->
LoginData.login = login
LoginData.password = password
start<MainActivity>()
finish()
}, {
scope.launch {
snackbarHostState
.showSnackbar(
message = "Error",
duration = SnackbarDuration.Short
)
}
})
Box(Modifier.padding(innerPadding).fillMaxSize(), Alignment.Center) {
LoginPanel(
Modifier.imePadding(),
{ login, password ->
LoginData.login = login
LoginData.password = password
start<MainActivity>()
finish()
},
{
scope.launch {
snackbarHostState
.showSnackbar(
message = "Error",
duration = SnackbarDuration.Short
)
}
}
)
}
}
}
}
@ -107,48 +118,52 @@ fun LoginPanel(
var loading by remember { mutableStateOf(false) }
val scope = rememberCoroutineScope()
Box(Modifier.fillMaxSize(), Alignment.BottomCenter) {
Column(
modifier
.padding(32.dp, 0.dp)
.clip(RoundedCornerShape(10.dp, 10.dp))
.background(MaterialTheme.colorScheme.primaryContainer)
.padding(16.dp)
) {
TextField(
modifier = Modifier.padding(8.dp),
value = login,
onValueChange = { login = it },
singleLine = true,
label = { Text(stringResource(R.string.prompt_login)) }
)
Column(
modifier
.padding(32.dp, 0.dp)
.clip(RoundedCornerShape(10.dp))
.background(MaterialTheme.colorScheme.surfaceContainer)
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
OutlinedTextField(
modifier = Modifier.padding(8.dp),
value = login,
onValueChange = { login = it },
singleLine = true,
label = { Text(stringResource(R.string.prompt_login))},
placeholder = { Text("s23380101") }
)
TextField(
modifier = Modifier.padding(8.dp),
value = password,
onValueChange = { password = it },
singleLine = true,
label = { Text(stringResource(R.string.prompt_password)) },
visualTransformation = PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password)
)
OutlinedTextField(
modifier = Modifier.padding(8.dp),
value = password,
onValueChange = { password = it },
singleLine = true,
label = { Text(stringResource(R.string.prompt_password)) },
visualTransformation = PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password)
)
Button(modifier = Modifier
.fillMaxWidth()
.padding(8.dp), onClick = {
Button(
modifier = Modifier.padding(8.dp),
enabled = login.trim().isNotEmpty() and password.trim().isNotEmpty() and !loading,
onClick = {
loading = true
scope.launch {
if (auth(login, password)) {
if (auth(login, password))
ok(login, password)
} else {
else
error()
}
loading = false
}
}) {
Text(stringResource(R.string.sign_in))
}
) {
if (loading)
CircularProgressIndicator()
else
Text(stringResource(R.string.sign_in))
}
}
}

View File

@ -38,6 +38,7 @@ import ru.sweetbread.unn.db.AppDatabase
import ru.sweetbread.unn.ui.composes.Blogposts
import ru.sweetbread.unn.ui.composes.Schedule
import ru.sweetbread.unn.ui.theme.UNNTheme
import splitties.arch.room.roomDb
import splitties.toast.toast
val client = HttpClient(Android) {
@ -62,18 +63,13 @@ val client = HttpClient(Android) {
}
}
lateinit var db: AppDatabase
val db = roomDb<AppDatabase>(name = "database")
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
db = Room.databaseBuilder(
applicationContext,
AppDatabase::class.java, "database"
).build()
setContent {
UNNTheme {
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {

View File

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name_reg">ННГУ</string>
<string name="app_name_dev">ННГУ Альфа</string>
<string name="app_name_beta">ННГУ Бета</string>
<string name="prompt_password">Пароль</string>
<string name="app_name">ННГУ</string>
<string name="prompt_login">Логин</string>
<string name="sign_in">Войти</string>
<string name="assistant">Ассистент</string>
@ -11,5 +13,5 @@
<string name="auditorium">Аудитория</string>
<string name="building">Здание</string>
<string name="floor">Этаж</string>
<string name="app_name_beta">ННГУ Бета</string>
<string name="noData">Нет данных</string>
</resources>

View File

@ -1,6 +1,8 @@
<resources>
<string name="app_name">UNN</string>
<!-- <string name="title_activity_login">LoginActivity</string>-->
<string name="app_name_reg">UNN</string>
<string name="app_name_dev">UNN Dev</string>
<string name="app_name_beta">UNN Beta</string>
<!-- <string name="title_activity_login">LoginActivity</string>-->
<string name="prompt_email" translatable="false">Email</string>
<string name="prompt_login">Login</string>
<string name="prompt_password">Password</string>
@ -13,7 +15,6 @@
<string name="building">Building</string>
<string name="floor">Floor</string>
<string name="lorem" translatable="false">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vel iaculis elit. Aliquam varius urna ut nisl rhoncus ullamcorper. Maecenas et nisl at dui mollis maximus nec in libero. Ut eu nulla id felis hendrerit lobortis. Maecenas vel facilisis lectus. Morbi eleifend massa a ante consequat, eu aliquam elit euismod. Aenean quis erat tincidunt, egestas ligula id, convallis tortor. Vivamus volutpat condimentum nisl sed eleifend. Aenean dapibus dolor ut orci lobortis, placerat lobortis tortor pretium. Nam eros lectus, convallis sed ultricies sit amet, lacinia sed sem. In mi odio, porta non malesuada et, cursus a metus. Morbi quis odio sed quam commodo gravida id sit amet dolor. Donec ac iaculis massa. Nulla mauris sapien, auctor consequat est in, tempus accumsan ipsum. Donec semper volutpat nisi. Quisque dignissim tellus ipsum, sed malesuada libero aliquam sed. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nam eleifend pharetra orci eu scelerisque. In hac habitasse platea dictumst. Sed non neque vitae metus porttitor vestibulum ut eget felis. Aliquam venenatis a magna eu mattis. Proin rutrum, sapien id viverra finibus, nisi quam aliquam eros, et dignissim lectus sem sit amet purus. Donec et semper enim, sed pretium lacus. Nullam venenatis ullamcorper maximus. Mauris pellentesque velit non sem sollicitudin molestie. Duis hendrerit consequat enim eget euismod.</string>
<string name="app_name_dev" translatable="false">UNN Dev</string>
<string name="app_name_beta">UNN Beta</string>
<string name="noData">No Data</string>
<!-- <string name="login_failed">"Login failed"</string>-->
</resources>

View File

@ -1,6 +1,6 @@
[versions]
acraHttp = "5.11.3"
agp = "8.5.2"
agp = "8.7.0"
calendar = "2.5.4"
coilCompose = "2.7.0"
compose = "1.6.4" # Updating this will cause an error!
@ -23,7 +23,7 @@ activity = "1.9.2"
navigationCompose = "2.7.7" # Updating this will cause an error!
roomRuntime = "2.6.1"
secretsGradlePlugin = "2.0.1"
splittiesFunPackAndroidBaseWithViewsDsl = "3.0.0"
splitties = "3.0.0"
kefirbb = "1.5"
[libraries]
@ -62,7 +62,8 @@ androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecy
androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle" }
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
secrets-gradle-plugin = { module = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin", version.ref = "secretsGradlePlugin" }
splitties-funpack-android-base-with-views-dsl = { module = "com.louiscad.splitties:splitties-fun-pack-android-base-with-views-dsl", version.ref = "splittiesFunPackAndroidBaseWithViewsDsl" }
splitties-base = { module = "com.louiscad.splitties:splitties-fun-pack-android-base-with-views-dsl", version.ref = "splitties" }
splitties-room = { module = "com.louiscad.splitties:splitties-arch-room", version.ref = "splitties" }
kefirbb = { group = "org.kefirsf", name = "kefirbb", version.ref = "kefirbb" }
[plugins]

View File

@ -1,6 +1,6 @@
#Sat Mar 16 18:30:45 MSK 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists