Init commit
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,88 @@
|
||||
plugins {
|
||||
alias(libs.plugins.androidApplication)
|
||||
alias(libs.plugins.jetbrainsKotlinAndroid)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "ru.sweetbread.unn"
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "ru.sweetbread.unn"
|
||||
minSdk = 26
|
||||
targetSdk = 34
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
viewBinding = true
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.5.1"
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring(libs.desugar.jdk.libs)
|
||||
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.ui)
|
||||
implementation(libs.androidx.ui.graphics)
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
implementation(libs.androidx.material3)
|
||||
implementation(libs.androidx.appcompat)
|
||||
implementation(libs.material)
|
||||
implementation(libs.androidx.annotation)
|
||||
implementation(libs.androidx.constraintlayout)
|
||||
implementation(libs.androidx.lifecycle.livedata.ktx)
|
||||
implementation(libs.androidx.lifecycle.viewmodel.ktx)
|
||||
implementation(libs.androidx.activity)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||
debugImplementation(libs.androidx.ui.tooling)
|
||||
debugImplementation(libs.androidx.ui.test.manifest)
|
||||
implementation(libs.androidx.core.splashscreen)
|
||||
|
||||
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
|
||||
implementation(libs.ktor.client.core)
|
||||
implementation(libs.ktor.client.cio)
|
||||
implementation(libs.ktor.client.logging)
|
||||
|
||||
implementation(libs.androidx.datastore.preferences)
|
||||
implementation(libs.splitties.funpack.android.base.with.views.dsl)
|
||||
|
||||
implementation(libs.compose)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,24 @@
|
||||
package ru.sweetbread.unn
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("ru.sweetbread.unn", appContext.packageName)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?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:label="@string/app_name"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.UNN"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".ui.layout.MainActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.UNN">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.layout.LoginActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.UNN"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,187 @@
|
||||
package ru.sweetbread.unn.ui
|
||||
|
||||
import io.ktor.client.request.forms.submitForm
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.request.parameter
|
||||
import io.ktor.client.statement.bodyAsText
|
||||
import io.ktor.http.parameters
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import ru.sweetbread.unn.ui.layout.LoginData
|
||||
import ru.sweetbread.unn.ui.layout.client
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
private lateinit var PHPSESSID: String
|
||||
lateinit var ME: User
|
||||
|
||||
const val portalURL = "https://portal.unn.ru"
|
||||
const val ruzapiURL = "$portalURL/ruzapi"
|
||||
|
||||
enum class Type(val s: String) {
|
||||
Student("student"),
|
||||
Group("group"),
|
||||
Lecturer("lecturer"),
|
||||
Auditorium("auditorium")
|
||||
}
|
||||
|
||||
enum class LecturerRank(val s: String) {
|
||||
Lecturer("Lecturer"),
|
||||
SLecturer("Senior Lecturer")
|
||||
}
|
||||
|
||||
class ScheduleUnit(val oid: Int,
|
||||
val auditorium: Auditorium,
|
||||
val date: LocalDate,
|
||||
val discipline: Discipline,
|
||||
val kindOfWork: KindOfWork,
|
||||
val lecturers: ArrayList<Lecturer>,
|
||||
val stream: String,
|
||||
val begin: LocalTime,
|
||||
val end: LocalTime)
|
||||
|
||||
class Auditorium( val name: String,
|
||||
val oid: Int,
|
||||
val floor: Int,
|
||||
val building: Building)
|
||||
class Building( val name: String,
|
||||
val gid: Int,
|
||||
val oid: Int)
|
||||
|
||||
class Discipline( val name: String,
|
||||
val oid: Int,
|
||||
val type: Int)
|
||||
|
||||
class KindOfWork( val name: String,
|
||||
val oid: Int,
|
||||
val uid: String,
|
||||
val complexity: Int)
|
||||
|
||||
class Lecturer( val name: String,
|
||||
val rank: LecturerRank,
|
||||
val email: String,
|
||||
val oid: Int,
|
||||
val uid: String)
|
||||
|
||||
class User (val id: String,
|
||||
val uns: String,
|
||||
val type: Type,
|
||||
val email: String,
|
||||
val name: String,
|
||||
val info: String)
|
||||
|
||||
/**
|
||||
* Authorize user by [login] and [password]
|
||||
*
|
||||
* Also defines local vars [PHPSESSID] and [ME]
|
||||
*/
|
||||
suspend fun auth(login: String = LoginData.login, password: String = LoginData.password, forced: Boolean = false): Boolean {
|
||||
if (!forced) {
|
||||
if (::PHPSESSID.isInitialized and ::ME.isInitialized)
|
||||
return true
|
||||
}
|
||||
val r = client.submitForm("$portalURL/auth/?login=yes",
|
||||
formParameters = parameters {
|
||||
append("AUTH_FORM", "Y")
|
||||
append("TYPE", "AUTH")
|
||||
append("backurl", "/")
|
||||
append("USER_LOGIN", login)
|
||||
append("USER_PASSWORD", password)
|
||||
}
|
||||
)
|
||||
if (r.status.value == 302) {
|
||||
PHPSESSID = """PHPSESSID=([\w\d]+)""".toRegex().find(r.headers["Set-Cookie"]!!)!!.groupValues[1]
|
||||
getMyself(login)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Save info about current [User] in memory
|
||||
*/
|
||||
private suspend fun getMyself(login: String) {
|
||||
val r = client.get("$ruzapiURL/studentinfo") {
|
||||
parameter("uns", login.substring(1))
|
||||
}
|
||||
val json = JSONObject(r.bodyAsText())
|
||||
ME = User(
|
||||
id = json.getString("id"),
|
||||
uns = json.getString("uns"),
|
||||
type = when(json.getString("type")) {
|
||||
"lecturer" -> Type.Lecturer // ig,,,
|
||||
else -> Type.Student
|
||||
},
|
||||
email = json.getString("email"),
|
||||
name = json.getString("fio"),
|
||||
info = json.getString("info")
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun getSchedule(type: Type = Type.Student, id: String = ME.id, start: LocalDate, finish: LocalDate): ArrayList<ScheduleUnit> {
|
||||
val unnDatePattern = DateTimeFormatter.ofPattern("yyyy.MM.dd")
|
||||
|
||||
val r = client.get("$ruzapiURL/schedule/${type.s}/$id") {
|
||||
parameter("start", start.format(unnDatePattern))
|
||||
parameter("finish", finish.format(unnDatePattern))
|
||||
parameter("lng", "1")
|
||||
}
|
||||
val json = JSONArray(r.bodyAsText())
|
||||
|
||||
val out = arrayListOf<ScheduleUnit>()
|
||||
for (i in 0 until json.length()) {
|
||||
val unit = json.getJSONObject(i)
|
||||
val lecturesJson = unit.getJSONArray("listOfLecturers")
|
||||
val lecturers = arrayListOf<Lecturer>()
|
||||
|
||||
for (j in 0 until lecturesJson.length()) {
|
||||
val lecturer = lecturesJson.getJSONObject(j)
|
||||
lecturers.add(
|
||||
Lecturer(
|
||||
name = lecturer.getString("lecturer"),
|
||||
email = lecturer.getString("lecturerEmail"),
|
||||
oid = lecturer.getInt("lecturerOid"),
|
||||
uid = lecturer.getString("lecturerUID"),
|
||||
rank = when (lecturer.getString("lecturer_rank")) {
|
||||
"СТПРЕП" -> LecturerRank.SLecturer
|
||||
else -> LecturerRank.Lecturer
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
out.add(
|
||||
ScheduleUnit(
|
||||
oid = unit.getInt("lessonOid"),
|
||||
auditorium = Auditorium(
|
||||
name = unit.getString("auditorium"),
|
||||
oid = unit.getInt("auditoriumOid"),
|
||||
floor = unit.getInt("auditoriumfloor"),
|
||||
building = Building(
|
||||
name = unit.getString("building"),
|
||||
gid = unit.getInt("buildingGid"),
|
||||
oid = unit.getInt("buildingOid")
|
||||
)
|
||||
),
|
||||
date = LocalDate.parse(unit.getString("date"), unnDatePattern),
|
||||
discipline = Discipline(
|
||||
name = unit.getString("discipline"),
|
||||
oid = unit.getInt("disciplineOid"),
|
||||
type = unit.getInt("disciplinetypeload")
|
||||
),
|
||||
kindOfWork = KindOfWork(
|
||||
name = unit.getString("kindOfWork"),
|
||||
complexity = unit.getInt("kindOfWorkComplexity"),
|
||||
oid = unit.getInt("kindOfWorkOid"),
|
||||
uid = unit.getString("kindOfWorkUid")
|
||||
),
|
||||
lecturers = lecturers,
|
||||
stream = unit.getString("stream"),
|
||||
begin = LocalTime.parse(unit.getString("beginLesson"), DateTimeFormatter.ofPattern("HH:mm")),
|
||||
end = LocalTime.parse(unit.getString("endLesson"), DateTimeFormatter.ofPattern("HH:mm"))
|
||||
)
|
||||
)
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package ru.sweetbread.unn.ui.composes
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
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.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
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.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
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.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.zIndex
|
||||
import com.kizitonwose.calendar.compose.WeekCalendar
|
||||
import com.kizitonwose.calendar.compose.weekcalendar.rememberWeekCalendarState
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.sweetbread.unn.ui.Auditorium
|
||||
import ru.sweetbread.unn.ui.Building
|
||||
import ru.sweetbread.unn.ui.Discipline
|
||||
import ru.sweetbread.unn.ui.KindOfWork
|
||||
import ru.sweetbread.unn.ui.Lecturer
|
||||
import ru.sweetbread.unn.ui.LecturerRank
|
||||
import ru.sweetbread.unn.ui.ScheduleUnit
|
||||
import ru.sweetbread.unn.ui.getSchedule
|
||||
import ru.sweetbread.unn.ui.theme.UNNTheme
|
||||
import java.time.DayOfWeek
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
@Composable
|
||||
fun Schedule() {
|
||||
val state = rememberWeekCalendarState(
|
||||
firstDayOfWeek = DayOfWeek.MONDAY
|
||||
)
|
||||
|
||||
Column {
|
||||
var curDate by remember { mutableStateOf(LocalDate.now()) }
|
||||
WeekCalendar(
|
||||
state = state,
|
||||
dayContent = {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(vertical = 16.dp)
|
||||
.aspectRatio(1f) // This is important for square sizing!
|
||||
.offset(2.dp)
|
||||
.background(if (it.date == curDate) MaterialTheme.colorScheme.primaryContainer else MaterialTheme.colorScheme.secondaryContainer)
|
||||
.clickable(
|
||||
onClick = {
|
||||
curDate = it.date
|
||||
}
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Text(
|
||||
text = it.date.dayOfMonth.toString(),
|
||||
fontWeight = if (it.date == LocalDate.now()) FontWeight.Bold else null
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
ScheduleDay(date = curDate)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ScheduleDay(modifier: Modifier = Modifier, date: LocalDate) {
|
||||
val scope = rememberCoroutineScope()
|
||||
var loadedDate by remember { mutableStateOf(LocalDate.MIN) }
|
||||
val lessons = remember { mutableListOf<ScheduleUnit>() }
|
||||
|
||||
if (loadedDate == date) {
|
||||
Log.d("Loaded", "${date.format(DateTimeFormatter.ISO_DATE)} ${lessons.size}")
|
||||
LazyColumn (modifier) {
|
||||
items(lessons) {
|
||||
ScheduleItem(unit = it)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp),
|
||||
color = MaterialTheme.colorScheme.surfaceVariant,
|
||||
trackColor = MaterialTheme.colorScheme.secondary,
|
||||
)
|
||||
LaunchedEffect(date != loadedDate) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
lessons.clear()
|
||||
lessons.addAll(getSchedule(start = date, finish = date))
|
||||
loadedDate = date
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ScheduleItem(modifier: Modifier = Modifier, unit: ScheduleUnit) {
|
||||
Row (
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.padding(4.dp)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(MaterialTheme.colorScheme.primaryContainer)
|
||||
.padding(8.dp)
|
||||
){
|
||||
Column (Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = unit.discipline.name,
|
||||
fontWeight = FontWeight.Bold,
|
||||
modifier = Modifier.zIndex(1f),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(text = unit.kindOfWork.name, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||
Row (Modifier) {
|
||||
Text(text = unit.auditorium.name, fontWeight = FontWeight.Bold, modifier = Modifier.padding(end = 4.dp))
|
||||
Text(text = unit.auditorium.building.name, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
val begin = unit.begin.format(DateTimeFormatter.ofPattern("HH:mm"))
|
||||
val end = unit.end.format(DateTimeFormatter.ofPattern("HH:mm"))
|
||||
Text(begin.toString(), fontWeight = FontWeight.Bold)
|
||||
Text(end.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun ScheduleItemPreview() {
|
||||
val unit = ScheduleUnit(
|
||||
oid = 1,
|
||||
Auditorium(
|
||||
name = "с/з 1(110)",
|
||||
oid = 3752,
|
||||
floor = 0,
|
||||
building = Building(
|
||||
name = "Корпус 6",
|
||||
gid = 30,
|
||||
oid = 155
|
||||
),
|
||||
),
|
||||
date = LocalDate.of(2024, 3, 11),
|
||||
discipline = Discipline(
|
||||
name = "Физическая культура и спорт (элективная дисциплина)",
|
||||
oid = 67895,
|
||||
type = 0
|
||||
),
|
||||
kindOfWork = KindOfWork(
|
||||
name = "Практика (семинарские занятия)",
|
||||
oid = 261,
|
||||
uid = "281474976710661",
|
||||
complexity = 1
|
||||
),
|
||||
lecturers = arrayListOf(
|
||||
Lecturer(
|
||||
name = "Фамилия Имя Отчество",
|
||||
rank = LecturerRank.SLecturer,
|
||||
email = "",
|
||||
oid = 28000,
|
||||
uid = "51000"
|
||||
)
|
||||
),
|
||||
stream = "3823Б1ПР1|3823Б1ПР2|3823Б1ПР3|3823Б1ПР4|3823Б1ПР5-В-OUP",
|
||||
begin = LocalTime.of(10, 50),
|
||||
end = LocalTime.of(12, 20)
|
||||
)
|
||||
|
||||
UNNTheme {
|
||||
Surface(color = MaterialTheme.colorScheme.background) {
|
||||
ScheduleItem(unit = unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package ru.sweetbread.unn.ui.layout
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
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.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
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
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
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.draw.clip
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.sweetbread.unn.R
|
||||
import ru.sweetbread.unn.ui.auth
|
||||
import ru.sweetbread.unn.ui.theme.UNNTheme
|
||||
import splitties.activities.start
|
||||
|
||||
class LoginActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
UNNTheme {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
Scaffold(
|
||||
snackbarHost = {
|
||||
SnackbarHost(hostState = snackbarHostState)
|
||||
}
|
||||
) { innerPadding ->
|
||||
LoginPanel(Modifier.padding(innerPadding), { login, password ->
|
||||
LoginData.login = login
|
||||
LoginData.password = password
|
||||
start<MainActivity>()
|
||||
}, {
|
||||
scope.launch {
|
||||
|
||||
snackbarHostState
|
||||
.showSnackbar(
|
||||
message = "Error",
|
||||
duration = SnackbarDuration.Long
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LoginPanel(
|
||||
modifier: Modifier = Modifier,
|
||||
ok: (login: String, password: String) -> Unit,
|
||||
error: () -> Unit
|
||||
) {
|
||||
var login by rememberSaveable { mutableStateOf("") }
|
||||
var password by rememberSaveable { mutableStateOf("") }
|
||||
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)) }
|
||||
)
|
||||
|
||||
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)
|
||||
)
|
||||
|
||||
Button(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp), onClick = {
|
||||
loading = true
|
||||
scope.launch {
|
||||
if (auth(login, password)) {
|
||||
ok(login, password)
|
||||
} else {
|
||||
error()
|
||||
}
|
||||
loading = false
|
||||
}
|
||||
|
||||
}) {
|
||||
Text(stringResource(R.string.sign_in))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun LoginPreview() {
|
||||
UNNTheme {
|
||||
LoginPanel(ok = { _, _ -> }, error = {})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package ru.sweetbread.unn.ui.layout
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AccountBox
|
||||
import androidx.compose.material.icons.filled.DateRange
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
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.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
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.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.zIndex
|
||||
import androidx.navigation.NavType
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import com.kizitonwose.calendar.compose.WeekCalendar
|
||||
import com.kizitonwose.calendar.compose.weekcalendar.rememberWeekCalendarState
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.plugins.HttpRequestRetry
|
||||
import io.ktor.client.plugins.HttpTimeout
|
||||
import io.ktor.client.plugins.cache.HttpCache
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import ru.sweetbread.unn.ui.Auditorium
|
||||
import ru.sweetbread.unn.ui.Building
|
||||
import ru.sweetbread.unn.ui.Discipline
|
||||
import ru.sweetbread.unn.ui.KindOfWork
|
||||
import ru.sweetbread.unn.ui.Lecturer
|
||||
import ru.sweetbread.unn.ui.LecturerRank
|
||||
import ru.sweetbread.unn.ui.ScheduleUnit
|
||||
import ru.sweetbread.unn.ui.auth
|
||||
import ru.sweetbread.unn.ui.getSchedule
|
||||
import ru.sweetbread.unn.ui.theme.UNNTheme
|
||||
import splitties.activities.start
|
||||
import splitties.preferences.Preferences
|
||||
import splitties.toast.toast
|
||||
import java.time.DayOfWeek
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import io.ktor.client.plugins.logging.*
|
||||
import ru.sweetbread.unn.ui.Type
|
||||
import ru.sweetbread.unn.ui.composes.Schedule
|
||||
import ru.sweetbread.unn.ui.composes.ScheduleDay
|
||||
|
||||
object LoginData : Preferences("loginData") {
|
||||
var login by stringPref("login", "")
|
||||
var password by stringPref("password", "")
|
||||
}
|
||||
|
||||
val client = HttpClient {
|
||||
install(HttpCache)
|
||||
install(Logging) {
|
||||
logger = object : Logger {
|
||||
override fun log(message: String) {
|
||||
Log.i("Ktor", message)
|
||||
}
|
||||
}
|
||||
level = LogLevel.ALL
|
||||
}
|
||||
install(HttpTimeout) {
|
||||
socketTimeoutMillis = HttpTimeout.INFINITE_TIMEOUT_MS
|
||||
}
|
||||
install(HttpRequestRetry) {
|
||||
retryOnServerErrors(maxRetries = 3)
|
||||
exponentialDelay()
|
||||
modifyRequest { request ->
|
||||
request.headers.append("x-retry-count", retryCount.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
|
||||
if (LoginData.login.isEmpty() or LoginData.password.isEmpty()) start<LoginActivity>()
|
||||
runBlocking {
|
||||
if (!auth()) start<LoginActivity>()
|
||||
}
|
||||
|
||||
setContent {
|
||||
UNNTheme {
|
||||
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
|
||||
val navController = rememberNavController()
|
||||
var route by remember { mutableStateOf("home") }
|
||||
|
||||
Scaffold(
|
||||
bottomBar = {
|
||||
NavigationBar {
|
||||
NavigationBarItem(
|
||||
onClick = { toast("Not implemented") },
|
||||
icon = {
|
||||
Icon(
|
||||
Icons.Filled.Home,
|
||||
contentDescription = "Home"
|
||||
)
|
||||
},
|
||||
selected = route.startsWith("home")
|
||||
)
|
||||
|
||||
NavigationBarItem(
|
||||
onClick = {
|
||||
navController.navigate("journal/schedule")
|
||||
route = "journal/schedule" },
|
||||
icon = {
|
||||
Icon(
|
||||
Icons.Filled.DateRange,
|
||||
contentDescription = "Schedule"
|
||||
)
|
||||
},
|
||||
selected = route.startsWith("journal/")
|
||||
)
|
||||
|
||||
NavigationBarItem(
|
||||
onClick = { toast("Not implemented") },
|
||||
icon = {
|
||||
Icon(
|
||||
Icons.Filled.AccountBox,
|
||||
contentDescription = "Account"
|
||||
)
|
||||
},
|
||||
selected = false
|
||||
)
|
||||
}
|
||||
}
|
||||
) {innerPadding ->
|
||||
Box(Modifier.padding(innerPadding)) {
|
||||
NavHost(navController, startDestination = "home/blogposts") {
|
||||
composable("home/blogposts") {
|
||||
Text("Not implemented")
|
||||
}
|
||||
composable("journal/schedule") {
|
||||
Schedule()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package ru.sweetbread.unn.ui.theme
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val md_theme_light_primary = Color(0xFF345CA8)
|
||||
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
|
||||
val md_theme_light_primaryContainer = Color(0xFFD9E2FF)
|
||||
val md_theme_light_onPrimaryContainer = Color(0xFF001A43)
|
||||
val md_theme_light_secondary = Color(0xFF575E71)
|
||||
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
|
||||
val md_theme_light_secondaryContainer = Color(0xFFDBE2F9)
|
||||
val md_theme_light_onSecondaryContainer = Color(0xFF141B2C)
|
||||
val md_theme_light_tertiary = Color(0xFF725573)
|
||||
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
|
||||
val md_theme_light_tertiaryContainer = Color(0xFFFCD7FB)
|
||||
val md_theme_light_onTertiaryContainer = Color(0xFF2A132D)
|
||||
val md_theme_light_error = Color(0xFFBA1A1A)
|
||||
val md_theme_light_errorContainer = Color(0xFFFFDAD6)
|
||||
val md_theme_light_onError = Color(0xFFFFFFFF)
|
||||
val md_theme_light_onErrorContainer = Color(0xFF410002)
|
||||
val md_theme_light_background = Color(0xFFFEFBFF)
|
||||
val md_theme_light_onBackground = Color(0xFF1B1B1F)
|
||||
val md_theme_light_surface = Color(0xFFFEFBFF)
|
||||
val md_theme_light_onSurface = Color(0xFF1B1B1F)
|
||||
val md_theme_light_surfaceVariant = Color(0xFFE1E2EC)
|
||||
val md_theme_light_onSurfaceVariant = Color(0xFF44464F)
|
||||
val md_theme_light_outline = Color(0xFF757780)
|
||||
val md_theme_light_inverseOnSurface = Color(0xFFF2F0F4)
|
||||
val md_theme_light_inverseSurface = Color(0xFF303034)
|
||||
val md_theme_light_inversePrimary = Color(0xFFAFC6FF)
|
||||
val md_theme_light_shadow = Color(0xFF000000)
|
||||
val md_theme_light_surfaceTint = Color(0xFF345CA8)
|
||||
val md_theme_light_outlineVariant = Color(0xFFC5C6D0)
|
||||
val md_theme_light_scrim = Color(0xFF000000)
|
||||
|
||||
val md_theme_dark_primary = Color(0xFFAFC6FF)
|
||||
val md_theme_dark_onPrimary = Color(0xFF002D6C)
|
||||
val md_theme_dark_primaryContainer = Color(0xFF15448F)
|
||||
val md_theme_dark_onPrimaryContainer = Color(0xFFD9E2FF)
|
||||
val md_theme_dark_secondary = Color(0xFFBFC6DC)
|
||||
val md_theme_dark_onSecondary = Color(0xFF293042)
|
||||
val md_theme_dark_secondaryContainer = Color(0xFF3F4759)
|
||||
val md_theme_dark_onSecondaryContainer = Color(0xFFDBE2F9)
|
||||
val md_theme_dark_tertiary = Color(0xFFDFBBDE)
|
||||
val md_theme_dark_onTertiary = Color(0xFF402743)
|
||||
val md_theme_dark_tertiaryContainer = Color(0xFF583E5A)
|
||||
val md_theme_dark_onTertiaryContainer = Color(0xFFFCD7FB)
|
||||
val md_theme_dark_error = Color(0xFFFFB4AB)
|
||||
val md_theme_dark_errorContainer = Color(0xFF93000A)
|
||||
val md_theme_dark_onError = Color(0xFF690005)
|
||||
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
|
||||
val md_theme_dark_background = Color(0xFF1B1B1F)
|
||||
val md_theme_dark_onBackground = Color(0xFFE3E2E6)
|
||||
val md_theme_dark_surface = Color(0xFF1B1B1F)
|
||||
val md_theme_dark_onSurface = Color(0xFFE3E2E6)
|
||||
val md_theme_dark_surfaceVariant = Color(0xFF44464F)
|
||||
val md_theme_dark_onSurfaceVariant = Color(0xFFC5C6D0)
|
||||
val md_theme_dark_outline = Color(0xFF8F9099)
|
||||
val md_theme_dark_inverseOnSurface = Color(0xFF1B1B1F)
|
||||
val md_theme_dark_inverseSurface = Color(0xFFE3E2E6)
|
||||
val md_theme_dark_inversePrimary = Color(0xFF345CA8)
|
||||
val md_theme_dark_shadow = Color(0xFF000000)
|
||||
val md_theme_dark_surfaceTint = Color(0xFFAFC6FF)
|
||||
val md_theme_dark_outlineVariant = Color(0xFF44464F)
|
||||
val md_theme_dark_scrim = Color(0xFF000000)
|
||||
|
||||
|
||||
val seed = Color(0xFF1C4893)
|
||||
@@ -0,0 +1,96 @@
|
||||
package ru.sweetbread.unn.ui.theme
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
|
||||
private val LightColors = lightColorScheme(
|
||||
primary = md_theme_light_primary,
|
||||
onPrimary = md_theme_light_onPrimary,
|
||||
primaryContainer = md_theme_light_primaryContainer,
|
||||
onPrimaryContainer = md_theme_light_onPrimaryContainer,
|
||||
secondary = md_theme_light_secondary,
|
||||
onSecondary = md_theme_light_onSecondary,
|
||||
secondaryContainer = md_theme_light_secondaryContainer,
|
||||
onSecondaryContainer = md_theme_light_onSecondaryContainer,
|
||||
tertiary = md_theme_light_tertiary,
|
||||
onTertiary = md_theme_light_onTertiary,
|
||||
tertiaryContainer = md_theme_light_tertiaryContainer,
|
||||
onTertiaryContainer = md_theme_light_onTertiaryContainer,
|
||||
error = md_theme_light_error,
|
||||
errorContainer = md_theme_light_errorContainer,
|
||||
onError = md_theme_light_onError,
|
||||
onErrorContainer = md_theme_light_onErrorContainer,
|
||||
background = md_theme_light_background,
|
||||
onBackground = md_theme_light_onBackground,
|
||||
surface = md_theme_light_surface,
|
||||
onSurface = md_theme_light_onSurface,
|
||||
surfaceVariant = md_theme_light_surfaceVariant,
|
||||
onSurfaceVariant = md_theme_light_onSurfaceVariant,
|
||||
outline = md_theme_light_outline,
|
||||
inverseOnSurface = md_theme_light_inverseOnSurface,
|
||||
inverseSurface = md_theme_light_inverseSurface,
|
||||
inversePrimary = md_theme_light_inversePrimary,
|
||||
surfaceTint = md_theme_light_surfaceTint,
|
||||
outlineVariant = md_theme_light_outlineVariant,
|
||||
scrim = md_theme_light_scrim,
|
||||
)
|
||||
|
||||
|
||||
private val DarkColors = darkColorScheme(
|
||||
primary = md_theme_dark_primary,
|
||||
onPrimary = md_theme_dark_onPrimary,
|
||||
primaryContainer = md_theme_dark_primaryContainer,
|
||||
onPrimaryContainer = md_theme_dark_onPrimaryContainer,
|
||||
secondary = md_theme_dark_secondary,
|
||||
onSecondary = md_theme_dark_onSecondary,
|
||||
secondaryContainer = md_theme_dark_secondaryContainer,
|
||||
onSecondaryContainer = md_theme_dark_onSecondaryContainer,
|
||||
tertiary = md_theme_dark_tertiary,
|
||||
onTertiary = md_theme_dark_onTertiary,
|
||||
tertiaryContainer = md_theme_dark_tertiaryContainer,
|
||||
onTertiaryContainer = md_theme_dark_onTertiaryContainer,
|
||||
error = md_theme_dark_error,
|
||||
errorContainer = md_theme_dark_errorContainer,
|
||||
onError = md_theme_dark_onError,
|
||||
onErrorContainer = md_theme_dark_onErrorContainer,
|
||||
background = md_theme_dark_background,
|
||||
onBackground = md_theme_dark_onBackground,
|
||||
surface = md_theme_dark_surface,
|
||||
onSurface = md_theme_dark_onSurface,
|
||||
surfaceVariant = md_theme_dark_surfaceVariant,
|
||||
onSurfaceVariant = md_theme_dark_onSurfaceVariant,
|
||||
outline = md_theme_dark_outline,
|
||||
inverseOnSurface = md_theme_dark_inverseOnSurface,
|
||||
inverseSurface = md_theme_dark_inverseSurface,
|
||||
inversePrimary = md_theme_dark_inversePrimary,
|
||||
surfaceTint = md_theme_dark_surfaceTint,
|
||||
outlineVariant = md_theme_dark_outlineVariant,
|
||||
scrim = md_theme_dark_scrim,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun UNNTheme(
|
||||
useDarkTheme: Boolean = isSystemInDarkTheme(),
|
||||
content: @Composable() () -> Unit
|
||||
) {
|
||||
val dynamicColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
|
||||
val colors = when {
|
||||
dynamicColor && useDarkTheme -> dynamicDarkColorScheme(LocalContext.current)
|
||||
dynamicColor && !useDarkTheme -> dynamicLightColorScheme(LocalContext.current)
|
||||
useDarkTheme -> DarkColors
|
||||
else -> LightColors
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colors,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package ru.sweetbread.unn.ui.theme
|
||||
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// Set of Material typography styles to start with
|
||||
val Typography = Typography(
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
/* Other default text styles to override
|
||||
titleLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 22.sp,
|
||||
lineHeight = 28.sp,
|
||||
letterSpacing = 0.sp
|
||||
),
|
||||
labelSmall = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 11.sp,
|
||||
lineHeight = 16.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
*/
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 790 B |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 878 B |
|
After Width: | Height: | Size: 534 B |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<dimen name="activity_horizontal_margin">48dp</dimen>
|
||||
</resources>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="prompt_password">Пароль</string>
|
||||
<string name="prompt_login">Логин</string>
|
||||
<string name="sign_in">Войти</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<dimen name="activity_horizontal_margin">200dp</dimen>
|
||||
</resources>
|
||||
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<dimen name="activity_horizontal_margin">48dp</dimen>
|
||||
</resources>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<resources>
|
||||
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#1565AA</color>
|
||||
</resources>
|
||||
@@ -0,0 +1,9 @@
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">UNN</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>
|
||||
<string name="sign_in">Sign in</string>
|
||||
<!-- <string name="login_failed">"Login failed"</string>-->
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Theme.UNN" parent="android:Theme.Material.NoActionBar" />
|
||||
</resources>
|
||||