Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d619e25e71
|
||
|
|
c63ca8c1da | ||
|
|
4c1a9c8765 | ||
|
|
5b6660aa8d | ||
|
|
46987c6fa5 | ||
|
|
3138afb0de | ||
|
|
455a3cae4c | ||
|
|
172f997af6 | ||
|
|
dd88d4a81a | ||
|
|
8b57881e28 | ||
|
|
9e1e64f3fc | ||
|
|
83759f5a37 | ||
|
|
da7498bf02 | ||
|
|
a417d74154 | ||
|
|
c09947865d | ||
|
|
d4bb804f24 | ||
|
|
fd49b30efb | ||
|
|
6e85ca4bdc | ||
|
|
3cf7885d9e |
No files matched your search
+1
-6
@@ -1,12 +1,7 @@
|
|||||||
*.iml
|
*.iml
|
||||||
.gradle
|
.gradle
|
||||||
/local.properties
|
/local.properties
|
||||||
/.idea/caches
|
/.idea/
|
||||||
/.idea/libraries
|
|
||||||
/.idea/modules.xml
|
|
||||||
/.idea/workspace.xml
|
|
||||||
/.idea/navEditor.xml
|
|
||||||
/.idea/assetWizardSettings.xml
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/build
|
/build
|
||||||
/captures
|
/captures
|
||||||
|
|||||||
Generated
-3
@@ -1,3 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
Generated
-123
@@ -1,123 +0,0 @@
|
|||||||
<component name="ProjectCodeStyleConfiguration">
|
|
||||||
<code_scheme name="Project" version="173">
|
|
||||||
<JetCodeStyleSettings>
|
|
||||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
|
||||||
</JetCodeStyleSettings>
|
|
||||||
<codeStyleSettings language="XML">
|
|
||||||
<option name="FORCE_REARRANGE_MODE" value="1" />
|
|
||||||
<indentOptions>
|
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
|
||||||
</indentOptions>
|
|
||||||
<arrangement>
|
|
||||||
<rules>
|
|
||||||
<section>
|
|
||||||
<rule>
|
|
||||||
<match>
|
|
||||||
<AND>
|
|
||||||
<NAME>xmlns:android</NAME>
|
|
||||||
<XML_ATTRIBUTE />
|
|
||||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
|
||||||
</AND>
|
|
||||||
</match>
|
|
||||||
</rule>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<rule>
|
|
||||||
<match>
|
|
||||||
<AND>
|
|
||||||
<NAME>xmlns:.*</NAME>
|
|
||||||
<XML_ATTRIBUTE />
|
|
||||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
|
||||||
</AND>
|
|
||||||
</match>
|
|
||||||
<order>BY_NAME</order>
|
|
||||||
</rule>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<rule>
|
|
||||||
<match>
|
|
||||||
<AND>
|
|
||||||
<NAME>.*:id</NAME>
|
|
||||||
<XML_ATTRIBUTE />
|
|
||||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
|
||||||
</AND>
|
|
||||||
</match>
|
|
||||||
</rule>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<rule>
|
|
||||||
<match>
|
|
||||||
<AND>
|
|
||||||
<NAME>.*:name</NAME>
|
|
||||||
<XML_ATTRIBUTE />
|
|
||||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
|
||||||
</AND>
|
|
||||||
</match>
|
|
||||||
</rule>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<rule>
|
|
||||||
<match>
|
|
||||||
<AND>
|
|
||||||
<NAME>name</NAME>
|
|
||||||
<XML_ATTRIBUTE />
|
|
||||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
|
||||||
</AND>
|
|
||||||
</match>
|
|
||||||
</rule>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<rule>
|
|
||||||
<match>
|
|
||||||
<AND>
|
|
||||||
<NAME>style</NAME>
|
|
||||||
<XML_ATTRIBUTE />
|
|
||||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
|
||||||
</AND>
|
|
||||||
</match>
|
|
||||||
</rule>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<rule>
|
|
||||||
<match>
|
|
||||||
<AND>
|
|
||||||
<NAME>.*</NAME>
|
|
||||||
<XML_ATTRIBUTE />
|
|
||||||
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
|
||||||
</AND>
|
|
||||||
</match>
|
|
||||||
<order>BY_NAME</order>
|
|
||||||
</rule>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<rule>
|
|
||||||
<match>
|
|
||||||
<AND>
|
|
||||||
<NAME>.*</NAME>
|
|
||||||
<XML_ATTRIBUTE />
|
|
||||||
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
|
||||||
</AND>
|
|
||||||
</match>
|
|
||||||
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
|
||||||
</rule>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<rule>
|
|
||||||
<match>
|
|
||||||
<AND>
|
|
||||||
<NAME>.*</NAME>
|
|
||||||
<XML_ATTRIBUTE />
|
|
||||||
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
|
||||||
</AND>
|
|
||||||
</match>
|
|
||||||
<order>BY_NAME</order>
|
|
||||||
</rule>
|
|
||||||
</section>
|
|
||||||
</rules>
|
|
||||||
</arrangement>
|
|
||||||
</codeStyleSettings>
|
|
||||||
<codeStyleSettings language="kotlin">
|
|
||||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
|
||||||
</codeStyleSettings>
|
|
||||||
</code_scheme>
|
|
||||||
</component>
|
|
||||||
Generated
-5
@@ -1,5 +0,0 @@
|
|||||||
<component name="ProjectCodeStyleConfiguration">
|
|
||||||
<state>
|
|
||||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
|
||||||
</state>
|
|
||||||
</component>
|
|
||||||
Generated
-6
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="CompilerConfiguration">
|
|
||||||
<bytecodeTargetLevel target="17" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
Generated
-19
@@ -1,19 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="deploymentTargetDropDown">
|
|
||||||
<value>
|
|
||||||
<entry key="GreetingPreview">
|
|
||||||
<State />
|
|
||||||
</entry>
|
|
||||||
<entry key="GreetingPreview2">
|
|
||||||
<State />
|
|
||||||
</entry>
|
|
||||||
<entry key="LoginActivity">
|
|
||||||
<State />
|
|
||||||
</entry>
|
|
||||||
<entry key="app">
|
|
||||||
<State />
|
|
||||||
</entry>
|
|
||||||
</value>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
Generated
-19
@@ -1,19 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
|
||||||
<component name="GradleSettings">
|
|
||||||
<option name="linkedExternalProjectsSettings">
|
|
||||||
<GradleProjectSettings>
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
|
||||||
<option name="modules">
|
|
||||||
<set>
|
|
||||||
<option value="$PROJECT_DIR$" />
|
|
||||||
<option value="$PROJECT_DIR$/app" />
|
|
||||||
</set>
|
|
||||||
</option>
|
|
||||||
<option name="resolveExternalAnnotations" value="false" />
|
|
||||||
</GradleProjectSettings>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
Generated
-6
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="KotlinJpsPluginSettings">
|
|
||||||
<option name="version" value="1.9.0" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
Generated
-10
@@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectMigrations">
|
|
||||||
<option name="MigrateToGradleLocalJavaHome">
|
|
||||||
<set>
|
|
||||||
<option value="$PROJECT_DIR$" />
|
|
||||||
</set>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
Generated
-9
@@ -1,9 +0,0 @@
|
|||||||
<project version="4">
|
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
|
||||||
</component>
|
|
||||||
<component name="ProjectType">
|
|
||||||
<option name="id" value="Android" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
Generated
-6
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
+22
-1
@@ -1,8 +1,11 @@
|
|||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.androidApplication)
|
alias(libs.plugins.androidApplication)
|
||||||
alias(libs.plugins.jetbrainsKotlinAndroid)
|
alias(libs.plugins.jetbrainsKotlinAndroid)
|
||||||
id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
|
id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
|
||||||
|
id("com.google.devtools.ksp")
|
||||||
}
|
}
|
||||||
|
|
||||||
secrets {
|
secrets {
|
||||||
@@ -19,6 +22,7 @@ android {
|
|||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = "1.0"
|
versionName = "1.0"
|
||||||
|
setProperty("archivesBaseName", "$applicationId-v$versionCode($versionName)")
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
@@ -31,6 +35,18 @@ android {
|
|||||||
isMinifyEnabled = false
|
isMinifyEnabled = false
|
||||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||||
}
|
}
|
||||||
|
debug {
|
||||||
|
applicationIdSuffix = ".debug"
|
||||||
|
versionNameSuffix =
|
||||||
|
"-debug+${LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)}"
|
||||||
|
resValue("string", "app_name", "app_name_dev")
|
||||||
|
}
|
||||||
|
create("beta") {
|
||||||
|
versionNameSuffix =
|
||||||
|
"-beta+${LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)}"
|
||||||
|
resValue("string", "app_name", "@string/app_name_beta")
|
||||||
|
signingConfig = signingConfigs.getByName("debug")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
@@ -84,6 +100,7 @@ dependencies {
|
|||||||
implementation(libs.ktor.client.core)
|
implementation(libs.ktor.client.core)
|
||||||
implementation(libs.ktor.client.cio)
|
implementation(libs.ktor.client.cio)
|
||||||
implementation(libs.ktor.client.logging)
|
implementation(libs.ktor.client.logging)
|
||||||
|
implementation(libs.ktor.client.android)
|
||||||
implementation(libs.coil.compose)
|
implementation(libs.coil.compose)
|
||||||
|
|
||||||
implementation(libs.androidx.datastore.preferences)
|
implementation(libs.androidx.datastore.preferences)
|
||||||
@@ -94,4 +111,8 @@ dependencies {
|
|||||||
implementation(libs.kefirbb)
|
implementation(libs.kefirbb)
|
||||||
|
|
||||||
implementation(libs.acra.http)
|
implementation(libs.acra.http)
|
||||||
|
|
||||||
|
implementation(libs.androidx.room.runtime)
|
||||||
|
implementation(libs.androidx.room.ktx)
|
||||||
|
ksp(libs.androidx.room.compiler)
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".ui.UNNApp"
|
android:name=".UNNApp"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
|||||||
@@ -0,0 +1,399 @@
|
|||||||
|
package ru.sweetbread.unn
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import io.ktor.client.request.forms.submitForm
|
||||||
|
import io.ktor.client.request.get
|
||||||
|
import io.ktor.client.request.header
|
||||||
|
import io.ktor.client.request.parameter
|
||||||
|
import io.ktor.client.statement.bodyAsText
|
||||||
|
import io.ktor.http.parameters
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONObject
|
||||||
|
import ru.sweetbread.unn.db.cacheSchedule
|
||||||
|
import ru.sweetbread.unn.db.cacheUser
|
||||||
|
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
|
||||||
|
|
||||||
|
/*
|
||||||
|
vuzAPI:
|
||||||
|
/profile/current -> Gets profile of current user
|
||||||
|
*/
|
||||||
|
|
||||||
|
private lateinit var PHPSESSID: String
|
||||||
|
private lateinit var CSRF: String
|
||||||
|
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"),
|
||||||
|
Lecturer("lecturer"),
|
||||||
|
Auditorium("auditorium"),
|
||||||
|
Employee("employee")
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class LecturerRank(val id: Int) {
|
||||||
|
Assistant(R.string.assistant),
|
||||||
|
Lecturer(R.string.lecturer),
|
||||||
|
SLecturer(R.string.slecturer),
|
||||||
|
AProfessor(R.string.aprofessor)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 unnId: Int,
|
||||||
|
val uid: String
|
||||||
|
)
|
||||||
|
|
||||||
|
class User(
|
||||||
|
val unnId: Int?,
|
||||||
|
val bitrixId: Int,
|
||||||
|
val userId: Int,
|
||||||
|
val type: Type,
|
||||||
|
val email: String,
|
||||||
|
val nameRu: String,
|
||||||
|
val nameEn: String,
|
||||||
|
val isMale: Boolean,
|
||||||
|
val birthday: LocalDate,
|
||||||
|
val avatar: AvatarSet
|
||||||
|
)
|
||||||
|
|
||||||
|
class Post(
|
||||||
|
val id: Int,
|
||||||
|
val authorId: Int,
|
||||||
|
val enableComments: Boolean,
|
||||||
|
val numComments: Int,
|
||||||
|
val date: LocalDateTime,
|
||||||
|
val content: String
|
||||||
|
)
|
||||||
|
|
||||||
|
class AvatarSet(
|
||||||
|
val original: String,
|
||||||
|
val thumbnail: String,
|
||||||
|
val small: 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) {
|
||||||
|
Log.d("tmp", "158")
|
||||||
|
PHPSESSID =
|
||||||
|
"""PHPSESSID=([\w\d]+)""".toRegex().find(r.headers["Set-Cookie"]!!)!!.groupValues[1]
|
||||||
|
getMyself(login)
|
||||||
|
getCSRF()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save info about current [User] in memory
|
||||||
|
*/
|
||||||
|
private suspend fun getMyself(login: String) {
|
||||||
|
Log.d("tmp", "getMyself")
|
||||||
|
// 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()
|
||||||
|
)
|
||||||
|
|
||||||
|
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(
|
||||||
|
type: Type = ME.type,
|
||||||
|
id: Int = ME.unnId!!,
|
||||||
|
date: LocalDate
|
||||||
|
): ArrayList<ScheduleUnit> {
|
||||||
|
|
||||||
|
if ((type == ME.type) and (id == ME.unnId!!)) {
|
||||||
|
val schedule = withContext(Dispatchers.IO) { loadSchedule(date) }
|
||||||
|
Log.d("Schedule", schedule.joinToString())
|
||||||
|
if (schedule.size != 0) {
|
||||||
|
return schedule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return getSchedule(type, id, date, date)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getSchedule(
|
||||||
|
type: Type = ME.type,
|
||||||
|
id: Int = ME.unnId!!,
|
||||||
|
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"),
|
||||||
|
unnId = lecturer.getInt("lecturerOid"),
|
||||||
|
uid = lecturer.getString("lecturerUID"),
|
||||||
|
rank = when (lecturer.getString("lecturer_rank")) {
|
||||||
|
"АССИСТ" -> LecturerRank.Assistant
|
||||||
|
"СТПРЕП" -> LecturerRank.SLecturer
|
||||||
|
"ДОЦЕНТ" -> LecturerRank.AProfessor
|
||||||
|
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")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((type == ME.type) and (id == ME.unnId!!)) {
|
||||||
|
cacheSchedule(out)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getCSRF() {
|
||||||
|
Log.d("tmp", "getCSRF")
|
||||||
|
val r = client.get("$restURL/log.blogpost.get") {
|
||||||
|
header("Cookie", "PHPSESSID=$PHPSESSID")
|
||||||
|
parameter("sessid", "")
|
||||||
|
}
|
||||||
|
CSRF = JSONObject(r.bodyAsText()).getString("sessid")
|
||||||
|
Log.d("tmp", "end getCSRF")
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getBlogposts(): ArrayList<Post> {
|
||||||
|
val r = client.get("$prtl2URL/news.php") {
|
||||||
|
header("Cookie", "PHPSESSID=$PHPSESSID")
|
||||||
|
header("x-bitrix-sessid-token", CSRF)
|
||||||
|
}
|
||||||
|
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(),
|
||||||
|
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("time"),
|
||||||
|
DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss")
|
||||||
|
),
|
||||||
|
content = el.getString("fulltext")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getUserByBitrixId(id: Int): User {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
loadUserByBitrixId(id)
|
||||||
|
}?.let { return it }
|
||||||
|
|
||||||
|
val userId = JSONObject(client.get("$vuzapiURL/user/bx/$id") {
|
||||||
|
header("Cookie", "PHPSESSID=$PHPSESSID")
|
||||||
|
}.bodyAsText()).getInt("id")
|
||||||
|
|
||||||
|
getUser(userId).let { user ->
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
cacheUser(user)
|
||||||
|
}
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getUser(id: Int): User {
|
||||||
|
val json = JSONObject(
|
||||||
|
client.get("$vuzapiURL/user/$id") {
|
||||||
|
header("Cookie", "PHPSESSID=$PHPSESSID")
|
||||||
|
}.bodyAsText()
|
||||||
|
)
|
||||||
|
|
||||||
|
Log.d("type", json.getJSONArray("profiles").getJSONObject(0).getString("type"))
|
||||||
|
|
||||||
|
return User(
|
||||||
|
unnId = null,
|
||||||
|
bitrixId = json.getInt("bitrix_id"),
|
||||||
|
userId = json.getInt("id"),
|
||||||
|
type = if (json.getJSONArray("profiles").getJSONObject(0)
|
||||||
|
.getString("type") == "employee"
|
||||||
|
) Type.Employee else Type.Student,
|
||||||
|
email = json.getString("email"),
|
||||||
|
nameRu = json.getString("fullname"),
|
||||||
|
nameEn = json.getString("fullname_en"),
|
||||||
|
isMale = json.getString("sex") == "M",
|
||||||
|
birthday = Instant
|
||||||
|
.parse(json.getString("birthdate"))
|
||||||
|
.atZone(ZoneId.of("Europe/Moscow"))
|
||||||
|
.toLocalDate(),
|
||||||
|
avatar = json.getJSONObject("photo").let {
|
||||||
|
AvatarSet(
|
||||||
|
it.getString("orig"),
|
||||||
|
it.getString("thumbnail"),
|
||||||
|
it.getString("small"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package ru.sweetbread.unn
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.Context
|
||||||
|
import org.acra.config.httpSender
|
||||||
|
import org.acra.data.StringFormat
|
||||||
|
import org.acra.ktx.initAcra
|
||||||
|
import org.acra.sender.HttpSender
|
||||||
|
|
||||||
|
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
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package ru.sweetbread.unn.db
|
||||||
|
|
||||||
|
import androidx.room.Database
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
|
|
||||||
|
@Database(entities = [
|
||||||
|
UserDB::class,
|
||||||
|
BuildingDB::class,
|
||||||
|
AuditoriumDB::class,
|
||||||
|
DisciplineDB::class,
|
||||||
|
KindOfWorkDB::class,
|
||||||
|
LecturerDB::class,
|
||||||
|
ScheduleUnitDB::class], version = 1)
|
||||||
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
abstract fun userDao(): UserDao
|
||||||
|
abstract fun scheduleDao(): ScheduleItemDao
|
||||||
|
abstract fun auditoriumDao(): AuditoriumDao
|
||||||
|
abstract fun buildingDao(): BuildingDao
|
||||||
|
abstract fun disciplineDao(): DisciplineDao
|
||||||
|
abstract fun kindOfWorkDao(): KindOfWorkDao
|
||||||
|
abstract fun lecturerDao(): LecturerDao
|
||||||
|
}
|
||||||
@@ -0,0 +1,384 @@
|
|||||||
|
package ru.sweetbread.unn.db
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import androidx.room.Query
|
||||||
|
import ru.sweetbread.unn.Auditorium
|
||||||
|
import ru.sweetbread.unn.Building
|
||||||
|
import ru.sweetbread.unn.Discipline
|
||||||
|
import ru.sweetbread.unn.KindOfWork
|
||||||
|
import ru.sweetbread.unn.Lecturer
|
||||||
|
import ru.sweetbread.unn.LecturerRank
|
||||||
|
import ru.sweetbread.unn.ScheduleUnit
|
||||||
|
import ru.sweetbread.unn.ui.layout.db
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.LocalTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class BuildingDB(
|
||||||
|
@PrimaryKey val oid: Int,
|
||||||
|
@ColumnInfo val name: String,
|
||||||
|
@ColumnInfo val gid: Int,
|
||||||
|
@ColumnInfo val expiredAt: String
|
||||||
|
)
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface BuildingDao {
|
||||||
|
@Query("SELECT * FROM buildingdb WHERE oid = :oid LIMIT 1")
|
||||||
|
fun get(oid: Int): BuildingDB?
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun insert(building: BuildingDB)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(building: BuildingDB)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cacheBuilding(building: Building) {
|
||||||
|
db.buildingDao().insert(
|
||||||
|
BuildingDB(
|
||||||
|
building.oid,
|
||||||
|
building.name,
|
||||||
|
building.gid,
|
||||||
|
LocalDateTime.now().plusMonths(1).format(DateTimeFormatter.ISO_DATE_TIME)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadBuilding(oid: Int): Building? {
|
||||||
|
return db.buildingDao().get(oid)?.let {
|
||||||
|
if (LocalDateTime.parse(
|
||||||
|
it.expiredAt,
|
||||||
|
DateTimeFormatter.ISO_DATE_TIME
|
||||||
|
) > LocalDateTime.now()
|
||||||
|
) {
|
||||||
|
db.buildingDao().delete(it)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
Building(
|
||||||
|
it.name,
|
||||||
|
it.gid,
|
||||||
|
it.oid
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class AuditoriumDB(
|
||||||
|
@PrimaryKey val oid: Int,
|
||||||
|
@ColumnInfo val name: String,
|
||||||
|
@ColumnInfo val floor: Int?,
|
||||||
|
@ColumnInfo val buildingOid: Int,
|
||||||
|
@ColumnInfo val expiredAt: String
|
||||||
|
)
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface AuditoriumDao {
|
||||||
|
@Query("SELECT * FROM auditoriumdb WHERE oid = :oid LIMIT 1")
|
||||||
|
fun get(oid: Int): AuditoriumDB?
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun insert(auditorium: AuditoriumDB)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(auditorium: AuditoriumDB)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cacheAuditorium(auditorium: Auditorium) {
|
||||||
|
cacheBuilding(auditorium.building)
|
||||||
|
db.auditoriumDao().insert(
|
||||||
|
AuditoriumDB(
|
||||||
|
auditorium.oid,
|
||||||
|
auditorium.name,
|
||||||
|
auditorium.floor,
|
||||||
|
auditorium.building.oid,
|
||||||
|
LocalDateTime.now().plusMonths(1).format(DateTimeFormatter.ISO_DATE_TIME)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadAuditorium(oid: Int): Auditorium? {
|
||||||
|
return db.auditoriumDao().get(oid)?.let {
|
||||||
|
if (LocalDateTime.parse(
|
||||||
|
it.expiredAt,
|
||||||
|
DateTimeFormatter.ISO_DATE_TIME
|
||||||
|
) > LocalDateTime.now()
|
||||||
|
) {
|
||||||
|
db.auditoriumDao().delete(it)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val building = loadBuilding(it.buildingOid) ?: return null
|
||||||
|
return Auditorium(
|
||||||
|
it.name,
|
||||||
|
it.oid,
|
||||||
|
it.floor ?: 0,
|
||||||
|
building
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class DisciplineDB(
|
||||||
|
@PrimaryKey val oid: Int,
|
||||||
|
@ColumnInfo val name: String,
|
||||||
|
@ColumnInfo val type: Int,
|
||||||
|
@ColumnInfo val expiredAt: String
|
||||||
|
)
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface DisciplineDao {
|
||||||
|
@Query("SELECT * FROM disciplinedb WHERE oid = :oid LIMIT 1")
|
||||||
|
fun get(oid: Int): DisciplineDB?
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun insert(discipline: DisciplineDB)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(discipline: DisciplineDB)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cacheDiscipline(discipline: Discipline) {
|
||||||
|
db.disciplineDao().insert(
|
||||||
|
DisciplineDB(
|
||||||
|
discipline.oid,
|
||||||
|
discipline.name,
|
||||||
|
discipline.type,
|
||||||
|
LocalDateTime.now().plusMonths(1).format(DateTimeFormatter.ISO_DATE_TIME)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadDiscipline(oid: Int): Discipline? {
|
||||||
|
return db.disciplineDao().get(oid)?.let {
|
||||||
|
if (LocalDateTime.parse(
|
||||||
|
it.expiredAt,
|
||||||
|
DateTimeFormatter.ISO_DATE_TIME
|
||||||
|
) > LocalDateTime.now()
|
||||||
|
) {
|
||||||
|
db.disciplineDao().delete(it)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return Discipline(
|
||||||
|
it.name,
|
||||||
|
it.oid,
|
||||||
|
it.type
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class KindOfWorkDB(
|
||||||
|
@PrimaryKey val oid: Int,
|
||||||
|
@ColumnInfo val name: String,
|
||||||
|
@ColumnInfo val uid: String,
|
||||||
|
@ColumnInfo val complexity: Int,
|
||||||
|
@ColumnInfo val expiredAt: String
|
||||||
|
)
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface KindOfWorkDao {
|
||||||
|
@Query("SELECT * FROM kindofworkdb WHERE oid = :oid LIMIT 1")
|
||||||
|
fun get(oid: Int): KindOfWorkDB?
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun insert(kindOfWork: KindOfWorkDB)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(kindOfWork: KindOfWorkDB)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cacheKindOfWork(kindOfWork: KindOfWork) {
|
||||||
|
db.kindOfWorkDao().insert(
|
||||||
|
KindOfWorkDB(
|
||||||
|
kindOfWork.oid,
|
||||||
|
kindOfWork.name,
|
||||||
|
kindOfWork.uid,
|
||||||
|
kindOfWork.complexity,
|
||||||
|
LocalDateTime.now().plusMonths(1).format(DateTimeFormatter.ISO_DATE_TIME)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadKindOfWork(oid: Int): KindOfWork? {
|
||||||
|
return db.kindOfWorkDao().get(oid)?.let {
|
||||||
|
if (LocalDateTime.parse(
|
||||||
|
it.expiredAt,
|
||||||
|
DateTimeFormatter.ISO_DATE_TIME
|
||||||
|
) > LocalDateTime.now()
|
||||||
|
) {
|
||||||
|
db.kindOfWorkDao().delete(it)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return KindOfWork(
|
||||||
|
it.name,
|
||||||
|
it.oid,
|
||||||
|
it.uid,
|
||||||
|
it.complexity
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class LecturerDB(
|
||||||
|
@PrimaryKey val unnId: Int,
|
||||||
|
@ColumnInfo val name: String,
|
||||||
|
@ColumnInfo val rank: LecturerRank,
|
||||||
|
@ColumnInfo val email: String,
|
||||||
|
@ColumnInfo val uid: String,
|
||||||
|
@ColumnInfo val expiredAt: String
|
||||||
|
)
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface LecturerDao {
|
||||||
|
@Query("SELECT * FROM lecturerdb WHERE unnId = :unnId LIMIT 1")
|
||||||
|
fun get(unnId: Int): LecturerDB?
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun insert(lecturer: LecturerDB)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(lecturer: LecturerDB)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cacheLecturer(lecturer: Lecturer) {
|
||||||
|
db.lecturerDao().insert(
|
||||||
|
LecturerDB(
|
||||||
|
lecturer.unnId,
|
||||||
|
lecturer.name,
|
||||||
|
lecturer.rank,
|
||||||
|
lecturer.email,
|
||||||
|
lecturer.uid,
|
||||||
|
LocalDateTime.now().plusMonths(1).format(DateTimeFormatter.ISO_DATE_TIME)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadLecturer(unnId: Int): Lecturer? {
|
||||||
|
return db.lecturerDao().get(unnId)?.let {
|
||||||
|
if (LocalDateTime.parse(
|
||||||
|
it.expiredAt,
|
||||||
|
DateTimeFormatter.ISO_DATE_TIME
|
||||||
|
) > LocalDateTime.now()
|
||||||
|
) {
|
||||||
|
db.lecturerDao().delete(it)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return Lecturer(
|
||||||
|
it.name,
|
||||||
|
it.rank,
|
||||||
|
it.email,
|
||||||
|
it.unnId,
|
||||||
|
it.uid
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class ScheduleUnitDB(
|
||||||
|
@PrimaryKey val oid: Int,
|
||||||
|
@ColumnInfo val date: String,
|
||||||
|
@ColumnInfo val stream: String,
|
||||||
|
@ColumnInfo val begin: String,
|
||||||
|
@ColumnInfo val end: String,
|
||||||
|
|
||||||
|
@ColumnInfo val auditoriumOid: Int,
|
||||||
|
@ColumnInfo val disciplineOid: Int,
|
||||||
|
@ColumnInfo val kindOfWorkOid: Int,
|
||||||
|
@ColumnInfo val lecturerId: Int, // TODO: many-to-many
|
||||||
|
|
||||||
|
@ColumnInfo val expiredAt: String
|
||||||
|
)
|
||||||
|
@Dao
|
||||||
|
interface ScheduleItemDao {
|
||||||
|
@Query("SELECT * FROM scheduleUnitDB WHERE oid = :oid LIMIT 1")
|
||||||
|
fun getSchedule(oid: Int): ScheduleUnitDB?
|
||||||
|
|
||||||
|
@Query("SELECT * FROM scheduleUnitDB WHERE date = :date ORDER BY `begin`")
|
||||||
|
fun getSchedule(date: String): List<ScheduleUnitDB>
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun insert(user: ScheduleUnitDB)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(user: ScheduleUnitDB)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cacheSchedule(item: ScheduleUnit) {
|
||||||
|
cacheAuditorium(item.auditorium)
|
||||||
|
cacheDiscipline(item.discipline)
|
||||||
|
cacheKindOfWork(item.kindOfWork)
|
||||||
|
cacheLecturer(item.lecturers[0])
|
||||||
|
|
||||||
|
db.scheduleDao().insert(
|
||||||
|
ScheduleUnitDB(
|
||||||
|
item.oid,
|
||||||
|
item.date.format(DateTimeFormatter.ISO_DATE),
|
||||||
|
item.stream,
|
||||||
|
item.begin.format(DateTimeFormatter.ISO_TIME),
|
||||||
|
item.end.format(DateTimeFormatter.ISO_TIME),
|
||||||
|
item.auditorium.oid,
|
||||||
|
item.discipline.oid,
|
||||||
|
item.kindOfWork.oid,
|
||||||
|
item.lecturers[0].unnId,
|
||||||
|
when {
|
||||||
|
(LocalDate.now() > item.date) -> LocalDateTime.now().plusWeeks(2)
|
||||||
|
(LocalDate.now().plusWeeks(1) > item.date) -> LocalDateTime.now().plusDays(1)
|
||||||
|
(LocalDate.now().plusWeeks(2) > item.date) -> LocalDateTime.now().plusWeeks(1)
|
||||||
|
else -> LocalDateTime.now().plusWeeks(2)
|
||||||
|
}.format(DateTimeFormatter.ISO_DATE_TIME)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cacheSchedule(items: ArrayList<ScheduleUnit>) {
|
||||||
|
for (item in items)
|
||||||
|
cacheSchedule(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadSchedule(oid: Int): ScheduleUnit? {
|
||||||
|
db.scheduleDao().getSchedule(oid)?.let {
|
||||||
|
Log.d("load", it.oid.toString())
|
||||||
|
if (LocalDateTime.parse(
|
||||||
|
it.expiredAt,
|
||||||
|
DateTimeFormatter.ISO_DATE_TIME
|
||||||
|
) < LocalDateTime.now()
|
||||||
|
) {
|
||||||
|
Log.d("delete", it.oid.toString())
|
||||||
|
db.scheduleDao().delete(it)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return ScheduleUnit(
|
||||||
|
it.oid,
|
||||||
|
loadAuditorium(it.auditoriumOid) ?: return null,
|
||||||
|
LocalDate.parse(it.date, DateTimeFormatter.ISO_DATE),
|
||||||
|
loadDiscipline(it.disciplineOid) ?: return null,
|
||||||
|
loadKindOfWork(it.kindOfWorkOid) ?: return null,
|
||||||
|
arrayListOf(loadLecturer(it.lecturerId) ?: return null),
|
||||||
|
it.stream,
|
||||||
|
LocalTime.parse(it.begin, DateTimeFormatter.ISO_TIME),
|
||||||
|
LocalTime.parse(it.end, DateTimeFormatter.ISO_TIME)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadSchedule(date: LocalDate): ArrayList<ScheduleUnit> {
|
||||||
|
db.scheduleDao().getSchedule(date.format(DateTimeFormatter.ISO_DATE))
|
||||||
|
.mapNotNull { Log.d("meow", "${it.oid}: ${loadSchedule(it.oid)}") }
|
||||||
|
return ArrayList(
|
||||||
|
db.scheduleDao().getSchedule(date.format(DateTimeFormatter.ISO_DATE))
|
||||||
|
.mapNotNull { loadSchedule(it.oid) }
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package ru.sweetbread.unn.db
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import androidx.room.Query
|
||||||
|
import ru.sweetbread.unn.AvatarSet
|
||||||
|
import ru.sweetbread.unn.Type
|
||||||
|
import ru.sweetbread.unn.User
|
||||||
|
import ru.sweetbread.unn.ui.layout.db
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
data class UserDB(
|
||||||
|
@PrimaryKey val userId: Int,
|
||||||
|
@ColumnInfo val unnId: Int?,
|
||||||
|
@ColumnInfo val bitrixId: Int,
|
||||||
|
@ColumnInfo val type: Type,
|
||||||
|
@ColumnInfo val email: String,
|
||||||
|
@ColumnInfo val nameRu: String,
|
||||||
|
@ColumnInfo val nameEn: String,
|
||||||
|
@ColumnInfo val isMale: Boolean,
|
||||||
|
@ColumnInfo val birthday: String,
|
||||||
|
@ColumnInfo val origAvatar: String,
|
||||||
|
@ColumnInfo val thumbAvatar: String,
|
||||||
|
@ColumnInfo val smallAvatar: String,
|
||||||
|
@ColumnInfo val expiredAt: String
|
||||||
|
)
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface UserDao {
|
||||||
|
@Query("SELECT * FROM userDB WHERE bitrixId = :bitrixId LIMIT 1")
|
||||||
|
fun getUserByBitrix(bitrixId: Int): UserDB?
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insert(user: UserDB)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(user: UserDB)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun cacheUser(user: User) {
|
||||||
|
try {
|
||||||
|
db.userDao().insert(
|
||||||
|
UserDB(
|
||||||
|
user.userId,
|
||||||
|
user.unnId,
|
||||||
|
user.bitrixId,
|
||||||
|
user.type,
|
||||||
|
user.email,
|
||||||
|
user.nameRu,
|
||||||
|
user.nameEn,
|
||||||
|
user.isMale,
|
||||||
|
user.birthday.format(DateTimeFormatter.ISO_LOCAL_DATE),
|
||||||
|
user.avatar.original,
|
||||||
|
user.avatar.thumbnail,
|
||||||
|
user.avatar.small,
|
||||||
|
LocalDateTime.now().plusDays(1).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch (_: android.database.sqlite.SQLiteConstraintException) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadUserByBitrixId(bitrixId: Int): User? {
|
||||||
|
val user = db.userDao().getUserByBitrix(bitrixId)
|
||||||
|
Log.d("UserDB", user?.nameEn ?: "None")
|
||||||
|
if (user == null) return null
|
||||||
|
if (LocalDateTime.parse(
|
||||||
|
user.expiredAt,
|
||||||
|
DateTimeFormatter.ISO_LOCAL_DATE_TIME
|
||||||
|
) < LocalDateTime.now()
|
||||||
|
) {
|
||||||
|
db.userDao().delete(user)
|
||||||
|
return null
|
||||||
|
} else {
|
||||||
|
return User(
|
||||||
|
user.unnId,
|
||||||
|
user.bitrixId,
|
||||||
|
user.userId,
|
||||||
|
user.type,
|
||||||
|
user.email,
|
||||||
|
user.nameRu,
|
||||||
|
user.nameEn,
|
||||||
|
user.isMale,
|
||||||
|
LocalDate.parse(user.birthday, DateTimeFormatter.ISO_LOCAL_DATE),
|
||||||
|
AvatarSet(
|
||||||
|
user.origAvatar,
|
||||||
|
user.thumbAvatar,
|
||||||
|
user.smallAvatar
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,29 +1,24 @@
|
|||||||
package ru.sweetbread.unn.ui
|
package ru.sweetbread.unn.ui
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import io.ktor.client.request.forms.submitForm
|
import io.ktor.client.request.forms.submitForm
|
||||||
import io.ktor.client.request.get
|
import io.ktor.client.request.get
|
||||||
import io.ktor.client.request.header
|
|
||||||
import io.ktor.client.request.parameter
|
import io.ktor.client.request.parameter
|
||||||
import io.ktor.client.statement.bodyAsText
|
import io.ktor.client.statement.bodyAsText
|
||||||
import io.ktor.http.parameters
|
import io.ktor.http.parameters
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import ru.sweetbread.unn.R
|
|
||||||
import ru.sweetbread.unn.ui.layout.LoginData
|
import ru.sweetbread.unn.ui.layout.LoginData
|
||||||
import ru.sweetbread.unn.ui.layout.client
|
import ru.sweetbread.unn.ui.layout.client
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
|
||||||
import java.time.LocalTime
|
import java.time.LocalTime
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
private lateinit var PHPSESSID: String
|
private lateinit var PHPSESSID: String
|
||||||
private lateinit var CSRF: String
|
|
||||||
lateinit var ME: User
|
lateinit var ME: User
|
||||||
|
|
||||||
const val portalURL = "https://portal.unn.ru"
|
const val portalURL = "https://portal.unn.ru"
|
||||||
const val ruzapiURL = "$portalURL/ruzapi"
|
const val ruzapiURL = "$portalURL/ruzapi"
|
||||||
const val vuzapiURL = "$portalURL/bitrix/vuz/api"
|
|
||||||
const val restURL = "$portalURL/rest"
|
|
||||||
|
|
||||||
enum class Type(val s: String) {
|
enum class Type(val s: String) {
|
||||||
Student("student"),
|
Student("student"),
|
||||||
@@ -32,73 +27,55 @@ enum class Type(val s: String) {
|
|||||||
Auditorium("auditorium")
|
Auditorium("auditorium")
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class LecturerRank(val id: Int) {
|
enum class LecturerRank(val s: String) {
|
||||||
Assistant(R.string.assistant),
|
Lecturer("Lecturer"),
|
||||||
Lecturer(R.string.lecturer),
|
SLecturer("Senior Lecturer")
|
||||||
SLecturer(R.string.slecturer),
|
|
||||||
AProfessor(R.string.aprofessor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ScheduleUnit(val oid: Int,
|
class ScheduleUnit(
|
||||||
val auditorium: Auditorium,
|
val auditorium: Auditorium,
|
||||||
val date: LocalDate,
|
val date: LocalDate,
|
||||||
val discipline: Discipline,
|
val discipline: Discipline,
|
||||||
val kindOfWork: KindOfWork,
|
val kindOfWork: KindOfWork,
|
||||||
val lecturers: ArrayList<Lecturer>,
|
val lecturers: ArrayList<Lecturer>,
|
||||||
val stream: String,
|
val stream: String,
|
||||||
val begin: LocalTime,
|
val begin: LocalTime,
|
||||||
val end: LocalTime)
|
val end: LocalTime)
|
||||||
|
|
||||||
class Auditorium( val name: String,
|
class Auditorium (val name: String,
|
||||||
val oid: Int,
|
val oid: Int,
|
||||||
val floor: Int,
|
val floor: Int,
|
||||||
val building: Building)
|
val building: Building)
|
||||||
class Building( val name: String,
|
class Building(val name: String,
|
||||||
val gid: Int,
|
val gid: Int,
|
||||||
val oid: Int)
|
val oid: Int)
|
||||||
|
|
||||||
class Discipline( val name: String,
|
class Discipline (val name: String,
|
||||||
val oid: Int,
|
val oid: Int,
|
||||||
val type: Int)
|
val type: Int)
|
||||||
|
|
||||||
class KindOfWork( val name: String,
|
class KindOfWork (val name: String,
|
||||||
val oid: Int,
|
val oid: Int,
|
||||||
val uid: String,
|
val uid: String,
|
||||||
val complexity: Int)
|
val complexity: Int)
|
||||||
|
|
||||||
class Lecturer( val name: String,
|
class Lecturer (val name: String,
|
||||||
val rank: LecturerRank,
|
val rank: LecturerRank,
|
||||||
val email: String,
|
val email: String,
|
||||||
val unnId: Int,
|
val oid: Int,
|
||||||
val uid: String)
|
val uid: String)
|
||||||
|
|
||||||
class User (val unnId: Int?,
|
class User (val id: String,
|
||||||
val bitrixId: Int,
|
val uns: String,
|
||||||
val userId: Int,
|
|
||||||
val type: Type,
|
val type: Type,
|
||||||
val email: String,
|
val email: String,
|
||||||
val nameRu: String,
|
val name: String,
|
||||||
val nameEn: String,
|
val info: String)
|
||||||
val isMale: Boolean,
|
|
||||||
val birthday: LocalDate,
|
|
||||||
val avatar: ImageSet)
|
|
||||||
|
|
||||||
class Post(
|
|
||||||
val id: Int,
|
|
||||||
val authorId: Int,
|
|
||||||
val enableComments: Boolean,
|
|
||||||
val numComments: Int,
|
|
||||||
val date: LocalDateTime,
|
|
||||||
val content: String)
|
|
||||||
|
|
||||||
class ImageSet(val original: String,
|
|
||||||
val thumbnail: String,
|
|
||||||
val small: String)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authorize user by [login] and [password]
|
* Authorize user by [login] and [password]
|
||||||
*
|
*
|
||||||
* Also defines local vars [PHPSESSID] and [ME]
|
* Also defines local vars [PHPSESSID] and [ME.id]
|
||||||
*/
|
*/
|
||||||
suspend fun auth(login: String = LoginData.login, password: String = LoginData.password, forced: Boolean = false): Boolean {
|
suspend fun auth(login: String = LoginData.login, password: String = LoginData.password, forced: Boolean = false): Boolean {
|
||||||
if (!forced) {
|
if (!forced) {
|
||||||
@@ -117,67 +94,36 @@ suspend fun auth(login: String = LoginData.login, password: String = LoginData.p
|
|||||||
if (r.status.value == 302) {
|
if (r.status.value == 302) {
|
||||||
PHPSESSID = """PHPSESSID=([\w\d]+)""".toRegex().find(r.headers["Set-Cookie"]!!)!!.groupValues[1]
|
PHPSESSID = """PHPSESSID=([\w\d]+)""".toRegex().find(r.headers["Set-Cookie"]!!)!!.groupValues[1]
|
||||||
getMyself(login)
|
getMyself(login)
|
||||||
getCSRF()
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Save info about current [User] in memory
|
|
||||||
*/
|
|
||||||
private suspend fun getMyself(login: String) {
|
private suspend fun getMyself(login: String) {
|
||||||
val studentinfo = JSONObject(client.get("$ruzapiURL/studentinfo") {
|
val r = client.get("$ruzapiURL/studentinfo") {
|
||||||
parameter("uns", login.substring(1))
|
parameter("uns", login.substring(1))
|
||||||
}.bodyAsText())
|
}
|
||||||
|
val json = JSONObject(r.bodyAsText())
|
||||||
val user = JSONObject(
|
|
||||||
client.get("$vuzapiURL/user") {
|
|
||||||
header("Cookie", "PHPSESSID=${PHPSESSID}")
|
|
||||||
}.bodyAsText()
|
|
||||||
)
|
|
||||||
|
|
||||||
ME = User(
|
ME = User(
|
||||||
unnId = studentinfo.getString("id").toInt(),
|
id = json.getString("id"),
|
||||||
bitrixId = user.getInt("bitrix_id"),
|
uns = json.getString("uns"),
|
||||||
userId = user.getInt("id"),
|
type = when(json.getString("type")) {
|
||||||
type = when(studentinfo.getString("type")) {
|
"lecturer" -> Type.Lecturer // ig...
|
||||||
"lecturer" -> Type.Lecturer // ig,,,
|
|
||||||
else -> Type.Student
|
else -> Type.Student
|
||||||
},
|
},
|
||||||
email = user.getString("email"),
|
email = json.getString("email"),
|
||||||
nameRu = user.getString("fullname"),
|
name = json.getString("fio"),
|
||||||
nameEn = user.getString("fullname_en"),
|
info = json.getString("info")
|
||||||
isMale = user.getString("sex") == "M",
|
|
||||||
birthday = LocalDate.parse(
|
|
||||||
user.getString("birthdate"),
|
|
||||||
DateTimeFormatter.ofPattern("yyyy-MM-dd")
|
|
||||||
),
|
|
||||||
avatar = user.getJSONObject("photo").let {
|
|
||||||
ImageSet(
|
|
||||||
it.getString("orig"),
|
|
||||||
it.getString("thumbnail"),
|
|
||||||
it.getString("small"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getSchedule(
|
suspend fun getSchedule(type: Type = Type.Student, id: String = ME.id, start: LocalDate, finish: LocalDate): ArrayList<ScheduleUnit> {
|
||||||
type: Type = ME.type,
|
|
||||||
id: Int = ME.unnId!!,
|
|
||||||
start: LocalDate,
|
|
||||||
finish: LocalDate
|
|
||||||
): ArrayList<ScheduleUnit> {
|
|
||||||
val unnDatePattern = DateTimeFormatter.ofPattern("yyyy.MM.dd")
|
|
||||||
|
|
||||||
val r = client.get("$ruzapiURL/schedule/${type.s}/$id") {
|
val r = client.get("$ruzapiURL/schedule/${type.s}/$id") {
|
||||||
parameter("start", start.format(unnDatePattern))
|
parameter("start", start.format(DateTimeFormatter.ofPattern("yyyy.MM.dd")))
|
||||||
parameter("finish", finish.format(unnDatePattern))
|
parameter("finish", finish.format(DateTimeFormatter.ofPattern("yyyy.MM.dd")))
|
||||||
parameter("lng", "1")
|
parameter("lng", "1")
|
||||||
}
|
}
|
||||||
val json = JSONArray(r.bodyAsText())
|
val json = JSONArray(r.bodyAsText())
|
||||||
|
|
||||||
val out = arrayListOf<ScheduleUnit>()
|
val out = arrayListOf<ScheduleUnit>()
|
||||||
for (i in 0 until json.length()) {
|
for (i in 0 until json.length()) {
|
||||||
val unit = json.getJSONObject(i)
|
val unit = json.getJSONObject(i)
|
||||||
@@ -190,12 +136,10 @@ suspend fun getSchedule(
|
|||||||
Lecturer(
|
Lecturer(
|
||||||
name = lecturer.getString("lecturer"),
|
name = lecturer.getString("lecturer"),
|
||||||
email = lecturer.getString("lecturerEmail"),
|
email = lecturer.getString("lecturerEmail"),
|
||||||
unnId = lecturer.getInt("lecturerOid"),
|
oid = lecturer.getInt("lecturerOid"),
|
||||||
uid = lecturer.getString("lecturerUID"),
|
uid = lecturer.getString("lecturerUID"),
|
||||||
rank = when (lecturer.getString("lecturer_rank")) {
|
rank = when (lecturer.getString("lecturer_rank")) {
|
||||||
"АССИСТ" -> LecturerRank.Assistant
|
|
||||||
"СТПРЕП" -> LecturerRank.SLecturer
|
"СТПРЕП" -> LecturerRank.SLecturer
|
||||||
"ДОЦЕНТ" -> LecturerRank.AProfessor
|
|
||||||
else -> LecturerRank.Lecturer
|
else -> LecturerRank.Lecturer
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -204,7 +148,6 @@ suspend fun getSchedule(
|
|||||||
|
|
||||||
out.add(
|
out.add(
|
||||||
ScheduleUnit(
|
ScheduleUnit(
|
||||||
oid = unit.getInt("lessonOid"),
|
|
||||||
auditorium = Auditorium(
|
auditorium = Auditorium(
|
||||||
name = unit.getString("auditorium"),
|
name = unit.getString("auditorium"),
|
||||||
oid = unit.getInt("auditoriumOid"),
|
oid = unit.getInt("auditoriumOid"),
|
||||||
@@ -215,7 +158,7 @@ suspend fun getSchedule(
|
|||||||
oid = unit.getInt("buildingOid")
|
oid = unit.getInt("buildingOid")
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
date = LocalDate.parse(unit.getString("date"), unnDatePattern),
|
date = LocalDate.parse(unit.getString("date"), DateTimeFormatter.ofPattern("yyyy.MM.dd")),
|
||||||
discipline = Discipline(
|
discipline = Discipline(
|
||||||
name = unit.getString("discipline"),
|
name = unit.getString("discipline"),
|
||||||
oid = unit.getInt("disciplineOid"),
|
oid = unit.getInt("disciplineOid"),
|
||||||
@@ -236,79 +179,3 @@ suspend fun getSchedule(
|
|||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getCSRF() {
|
|
||||||
val r = client.get("$restURL/log.blogpost.get") {
|
|
||||||
header("Cookie", "PHPSESSID=${PHPSESSID}")
|
|
||||||
parameter("sessid", "")
|
|
||||||
}
|
|
||||||
CSRF = JSONObject(r.bodyAsText()).getString("sessid")
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getBlogposts(): ArrayList<Post> {
|
|
||||||
val r = client.get("$restURL/log.blogpost.get") {
|
|
||||||
header("Cookie", "PHPSESSID=${PHPSESSID}")
|
|
||||||
parameter("sessid", CSRF)
|
|
||||||
}
|
|
||||||
val json = JSONObject(r.bodyAsText())
|
|
||||||
val result = json.getJSONArray("result")
|
|
||||||
|
|
||||||
val out = arrayListOf<Post>()
|
|
||||||
for (i in 0 until result.length()) {
|
|
||||||
val el = result.getJSONObject(i)
|
|
||||||
out.add(
|
|
||||||
Post(
|
|
||||||
id = el.getString("ID").toInt(),
|
|
||||||
authorId = el.getString("AUTHOR_ID").toInt(),
|
|
||||||
enableComments = el.getString("ENABLE_COMMENTS") == "Y",
|
|
||||||
numComments = el.getString("NUM_COMMENTS").toInt(),
|
|
||||||
date = LocalDateTime.parse(
|
|
||||||
el.getString("DATE_PUBLISH"),
|
|
||||||
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'+03:00'")
|
|
||||||
),
|
|
||||||
content = el.getString("DETAIL_TEXT")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getUserByBitrixId(id: Int): User {
|
|
||||||
val userId = JSONObject(client.get("$vuzapiURL/user/bx/$id") {
|
|
||||||
header("Cookie", "PHPSESSID=${PHPSESSID}")
|
|
||||||
}.bodyAsText()).getInt("id")
|
|
||||||
return getUser(userId)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getUser(id: Int): User {
|
|
||||||
val json = JSONObject(
|
|
||||||
client.get("$vuzapiURL/user/$id") {
|
|
||||||
header("Cookie", "PHPSESSID=${PHPSESSID}")
|
|
||||||
}.bodyAsText()
|
|
||||||
)
|
|
||||||
|
|
||||||
return User(
|
|
||||||
unnId = null,
|
|
||||||
bitrixId = json.getInt("bitrix_id"),
|
|
||||||
userId = json.getInt("id"),
|
|
||||||
type = when (json.getJSONArray("profiles").getJSONObject(0).getString("type")) {
|
|
||||||
"lecturer" -> Type.Lecturer // ig,,,
|
|
||||||
else -> Type.Student
|
|
||||||
},
|
|
||||||
email = json.getString("email"),
|
|
||||||
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")
|
|
||||||
),
|
|
||||||
avatar = json.getJSONObject("photo").let {
|
|
||||||
ImageSet(
|
|
||||||
it.getString("orig"),
|
|
||||||
it.getString("thumbnail"),
|
|
||||||
it.getString("small"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package ru.sweetbread.unn.ui
|
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.content.Context
|
|
||||||
import org.acra.config.httpSender
|
|
||||||
import org.acra.data.StringFormat
|
|
||||||
import org.acra.ktx.initAcra
|
|
||||||
import org.acra.sender.HttpSender
|
|
||||||
import ru.sweetbread.unn.BuildConfig
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,7 @@ package ru.sweetbread.unn.ui.composes
|
|||||||
import android.text.util.Linkify
|
import android.text.util.Linkify
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -23,13 +24,13 @@ import androidx.compose.runtime.LaunchedEffect
|
|||||||
import androidx.compose.runtime.NonRestartableComposable
|
import androidx.compose.runtime.NonRestartableComposable
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.graphics.toArgb
|
import androidx.compose.ui.graphics.toArgb
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
@@ -47,14 +48,14 @@ import kotlinx.coroutines.flow.StateFlow
|
|||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import org.kefirsf.bb.BBProcessorFactory
|
import org.kefirsf.bb.BBProcessorFactory
|
||||||
import org.kefirsf.bb.TextProcessor
|
import org.kefirsf.bb.TextProcessor
|
||||||
|
import ru.sweetbread.unn.AvatarSet
|
||||||
|
import ru.sweetbread.unn.Post
|
||||||
import ru.sweetbread.unn.R
|
import ru.sweetbread.unn.R
|
||||||
import ru.sweetbread.unn.ui.ImageSet
|
import ru.sweetbread.unn.Type
|
||||||
import ru.sweetbread.unn.ui.Post
|
import ru.sweetbread.unn.User
|
||||||
import ru.sweetbread.unn.ui.Type
|
import ru.sweetbread.unn.getBlogposts
|
||||||
import ru.sweetbread.unn.ui.User
|
import ru.sweetbread.unn.getUserByBitrixId
|
||||||
import ru.sweetbread.unn.ui.getBlogposts
|
import ru.sweetbread.unn.portalURL
|
||||||
import ru.sweetbread.unn.ui.getUserByBitrixId
|
|
||||||
import ru.sweetbread.unn.ui.portalURL
|
|
||||||
import ru.sweetbread.unn.ui.theme.UNNTheme
|
import ru.sweetbread.unn.ui.theme.UNNTheme
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
@@ -72,7 +73,7 @@ val defUser = User(
|
|||||||
"Jon Sigma Omega",
|
"Jon Sigma Omega",
|
||||||
true,
|
true,
|
||||||
LocalDate.now(),
|
LocalDate.now(),
|
||||||
ImageSet(
|
AvatarSet(
|
||||||
"https://upload.wikimedia.org/wikipedia/ru/thumb/9/94/%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg/500px-%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg",
|
"https://upload.wikimedia.org/wikipedia/ru/thumb/9/94/%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg/500px-%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg",
|
||||||
"https://upload.wikimedia.org/wikipedia/ru/thumb/9/94/%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg/500px-%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg",
|
"https://upload.wikimedia.org/wikipedia/ru/thumb/9/94/%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg/500px-%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg",
|
||||||
"https://upload.wikimedia.org/wikipedia/ru/thumb/9/94/%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg/500px-%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg"
|
"https://upload.wikimedia.org/wikipedia/ru/thumb/9/94/%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg/500px-%D0%93%D0%B8%D0%B3%D0%B0%D1%87%D0%B0%D0%B4.jpg"
|
||||||
@@ -83,6 +84,7 @@ val defUser = User(
|
|||||||
@Composable
|
@Composable
|
||||||
fun Blogposts(viewModel: PostViewModel = viewModel()) {
|
fun Blogposts(viewModel: PostViewModel = viewModel()) {
|
||||||
val posts by viewModel.posts.collectAsState()
|
val posts by viewModel.posts.collectAsState()
|
||||||
|
var extended by remember { mutableIntStateOf(0) }
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
viewModel.loadPosts()
|
viewModel.loadPosts()
|
||||||
@@ -96,8 +98,12 @@ fun Blogposts(viewModel: PostViewModel = viewModel()) {
|
|||||||
Modifier
|
Modifier
|
||||||
.padding(8.dp)
|
.padding(8.dp)
|
||||||
.clip(RoundedCornerShape(16.dp))
|
.clip(RoundedCornerShape(16.dp))
|
||||||
.background(MaterialTheme.colorScheme.secondaryContainer),
|
.background(MaterialTheme.colorScheme.secondaryContainer)
|
||||||
post = it
|
.clickable {
|
||||||
|
extended = if (extended == it.id) 0 else it.id
|
||||||
|
},
|
||||||
|
post = it,
|
||||||
|
extended == it.id
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,7 +167,7 @@ fun UserItem(modifier: Modifier = Modifier, user: User, info: String? = null) {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@NonRestartableComposable
|
@NonRestartableComposable
|
||||||
fun PostItem(modifier: Modifier = Modifier, post: Post) {
|
fun PostItem(modifier: Modifier = Modifier, post: Post, extended: Boolean = false) {
|
||||||
var user: User? by remember { mutableStateOf(null) }
|
var user: User? by remember { mutableStateOf(null) }
|
||||||
val processor = remember { BBProcessorFactory.getInstance().create() }
|
val processor = remember { BBProcessorFactory.getInstance().create() }
|
||||||
var html: String by remember { mutableStateOf("") }
|
var html: String by remember { mutableStateOf("") }
|
||||||
@@ -184,16 +190,20 @@ fun PostItem(modifier: Modifier = Modifier, post: Post) {
|
|||||||
trackColor = MaterialTheme.colorScheme.secondary,
|
trackColor = MaterialTheme.colorScheme.secondary,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val textColor = MaterialTheme.colorScheme.onBackground.toArgb()
|
||||||
|
val linkColor = MaterialTheme.colorScheme.primary.toArgb()
|
||||||
|
|
||||||
AndroidView(
|
AndroidView(
|
||||||
factory = {
|
factory = {
|
||||||
MaterialTextView(it).apply {
|
MaterialTextView(it).apply {
|
||||||
autoLinkMask = Linkify.WEB_URLS
|
autoLinkMask = Linkify.WEB_URLS
|
||||||
linksClickable = true
|
linksClickable = true
|
||||||
setLinkTextColor(Color.White.toArgb())
|
setTextColor(textColor)
|
||||||
|
setLinkTextColor(linkColor)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
update = {
|
update = {
|
||||||
it.maxLines = 25
|
it.maxLines = if (extended) Int.MAX_VALUE else 5
|
||||||
it.text = HtmlCompat.fromHtml(html, 0)
|
it.text = HtmlCompat.fromHtml(html, 0)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -212,13 +222,39 @@ private fun toHtml(
|
|||||||
processor: TextProcessor,
|
processor: TextProcessor,
|
||||||
post: Post
|
post: Post
|
||||||
): String {
|
): String {
|
||||||
val html = processor.process(post.content)
|
Log.d("toHTML | original", post.content)
|
||||||
return html.replace("""\[URL=(.+)](.+)\[/URL]""".toRegex()) {
|
|
||||||
Log.d("replace", it.groups.toString())
|
val result =
|
||||||
"<a href='${it.groups[1]?.value}'>${it.groups[2]?.value}</a>"
|
post.content.replace("""\[URL=(.+?)](.+?)\[/URL]""".toRegex(RegexOption.DOT_MATCHES_ALL)) {
|
||||||
}.replace("""(\[FONT=.+]|\[CENTER])(.+)(\[/FONT]|\[/CENTER])""".toRegex()) {
|
"<a href=\"${it.groups[1]?.value}\">${it.groups[2]?.value}</a>"
|
||||||
it.groups[2]?.value.toString()
|
}.replace("""\[FONT=(.+?)](.*?)\[/FONT]""".toRegex(RegexOption.DOT_MATCHES_ALL)) {
|
||||||
}.replace("""\[IMG .+].+\[/IMG]""".toRegex(), "")
|
"<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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -41,15 +41,15 @@ import com.kizitonwose.calendar.compose.WeekCalendar
|
|||||||
import com.kizitonwose.calendar.compose.weekcalendar.rememberWeekCalendarState
|
import com.kizitonwose.calendar.compose.weekcalendar.rememberWeekCalendarState
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import ru.sweetbread.unn.Auditorium
|
||||||
|
import ru.sweetbread.unn.Building
|
||||||
|
import ru.sweetbread.unn.Discipline
|
||||||
|
import ru.sweetbread.unn.KindOfWork
|
||||||
|
import ru.sweetbread.unn.Lecturer
|
||||||
|
import ru.sweetbread.unn.LecturerRank
|
||||||
import ru.sweetbread.unn.R
|
import ru.sweetbread.unn.R
|
||||||
import ru.sweetbread.unn.ui.Auditorium
|
import ru.sweetbread.unn.ScheduleUnit
|
||||||
import ru.sweetbread.unn.ui.Building
|
import ru.sweetbread.unn.getScheduleDay
|
||||||
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 ru.sweetbread.unn.ui.theme.UNNTheme
|
||||||
import java.time.DayOfWeek
|
import java.time.DayOfWeek
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
@@ -119,7 +119,7 @@ fun ScheduleDay(modifier: Modifier = Modifier, date: LocalDate) {
|
|||||||
LaunchedEffect(date != loadedDate) {
|
LaunchedEffect(date != loadedDate) {
|
||||||
scope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
lessons.clear()
|
lessons.clear()
|
||||||
lessons.addAll(getSchedule(start = date, finish = date))
|
lessons.addAll(getScheduleDay(date = date))
|
||||||
loadedDate = date
|
loadedDate = date
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import ru.sweetbread.unn.R
|
import ru.sweetbread.unn.R
|
||||||
import ru.sweetbread.unn.ui.auth
|
import ru.sweetbread.unn.auth
|
||||||
import ru.sweetbread.unn.ui.theme.UNNTheme
|
import ru.sweetbread.unn.ui.theme.UNNTheme
|
||||||
import splitties.activities.start
|
import splitties.activities.start
|
||||||
import splitties.preferences.Preferences
|
import splitties.preferences.Preferences
|
||||||
|
|||||||
@@ -25,20 +25,22 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import androidx.room.Room
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
|
import io.ktor.client.engine.android.Android
|
||||||
import io.ktor.client.plugins.HttpRequestRetry
|
import io.ktor.client.plugins.HttpRequestRetry
|
||||||
import io.ktor.client.plugins.HttpTimeout
|
import io.ktor.client.plugins.HttpTimeout
|
||||||
import io.ktor.client.plugins.cache.HttpCache
|
import io.ktor.client.plugins.cache.HttpCache
|
||||||
import io.ktor.client.plugins.logging.LogLevel
|
import io.ktor.client.plugins.logging.LogLevel
|
||||||
import io.ktor.client.plugins.logging.Logger
|
import io.ktor.client.plugins.logging.Logger
|
||||||
import io.ktor.client.plugins.logging.Logging
|
import io.ktor.client.plugins.logging.Logging
|
||||||
|
import ru.sweetbread.unn.db.AppDatabase
|
||||||
import ru.sweetbread.unn.ui.composes.Blogposts
|
import ru.sweetbread.unn.ui.composes.Blogposts
|
||||||
import ru.sweetbread.unn.ui.composes.Schedule
|
import ru.sweetbread.unn.ui.composes.Schedule
|
||||||
import ru.sweetbread.unn.ui.theme.UNNTheme
|
import ru.sweetbread.unn.ui.theme.UNNTheme
|
||||||
import splitties.toast.toast
|
import splitties.toast.toast
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
val client = HttpClient {
|
val client = HttpClient(Android) {
|
||||||
install(HttpCache)
|
install(HttpCache)
|
||||||
install(Logging) {
|
install(Logging) {
|
||||||
logger = object : Logger {
|
logger = object : Logger {
|
||||||
@@ -49,10 +51,10 @@ val client = HttpClient {
|
|||||||
level = LogLevel.ALL
|
level = LogLevel.ALL
|
||||||
}
|
}
|
||||||
install(HttpTimeout) {
|
install(HttpTimeout) {
|
||||||
socketTimeoutMillis = HttpTimeout.INFINITE_TIMEOUT_MS
|
connectTimeoutMillis = 5000
|
||||||
}
|
}
|
||||||
install(HttpRequestRetry) {
|
install(HttpRequestRetry) {
|
||||||
retryOnServerErrors(maxRetries = 3)
|
retryOnException(maxRetries = 3, retryOnTimeout = true)
|
||||||
exponentialDelay()
|
exponentialDelay()
|
||||||
modifyRequest { request ->
|
modifyRequest { request ->
|
||||||
request.headers.append("x-retry-count", retryCount.toString())
|
request.headers.append("x-retry-count", retryCount.toString())
|
||||||
@@ -60,12 +62,17 @@ val client = HttpClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val cacheDir = File("/data/data/ru.sweetbread.unn/files/cache")
|
lateinit var db: AppDatabase
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
Log.d("mkdir", cacheDir.mkdir().toString())
|
|
||||||
|
db = Room.databaseBuilder(
|
||||||
|
applicationContext,
|
||||||
|
AppDatabase::class.java, "database"
|
||||||
|
).build()
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
UNNTheme {
|
UNNTheme {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="prompt_password">Пароль</string>
|
<string name="prompt_password">Пароль</string>
|
||||||
|
<string name="app_name">ННГУ</string>
|
||||||
<string name="prompt_login">Логин</string>
|
<string name="prompt_login">Логин</string>
|
||||||
<string name="sign_in">Войти</string>
|
<string name="sign_in">Войти</string>
|
||||||
<string name="assistant">Ассистент</string>
|
<string name="assistant">Ассистент</string>
|
||||||
@@ -10,4 +11,5 @@
|
|||||||
<string name="auditorium">Аудитория</string>
|
<string name="auditorium">Аудитория</string>
|
||||||
<string name="building">Здание</string>
|
<string name="building">Здание</string>
|
||||||
<string name="floor">Этаж</string>
|
<string name="floor">Этаж</string>
|
||||||
|
<string name="app_name_beta">ННГУ Бета</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name" translatable="false">UNN</string>
|
<string name="app_name">UNN</string>
|
||||||
<!-- <string name="title_activity_login">LoginActivity</string>-->
|
<!-- <string name="title_activity_login">LoginActivity</string>-->
|
||||||
<string name="prompt_email" translatable="false">Email</string>
|
<string name="prompt_email" translatable="false">Email</string>
|
||||||
<string name="prompt_login">Login</string>
|
<string name="prompt_login">Login</string>
|
||||||
@@ -13,5 +13,7 @@
|
|||||||
<string name="building">Building</string>
|
<string name="building">Building</string>
|
||||||
<string name="floor">Floor</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="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="login_failed">"Login failed"</string>-->
|
<!-- <string name="login_failed">"Login failed"</string>-->
|
||||||
</resources>
|
</resources>
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.androidApplication) apply false
|
alias(libs.plugins.androidApplication) apply false
|
||||||
alias(libs.plugins.jetbrainsKotlinAndroid) apply false
|
alias(libs.plugins.jetbrainsKotlinAndroid) apply false
|
||||||
|
id("com.google.devtools.ksp") version "1.9.0-1.0.13" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
|
|||||||
+31
-31
@@ -1,30 +1,27 @@
|
|||||||
[versions]
|
[versions]
|
||||||
acraHttp = "5.11.3"
|
acraHttp = "5.11.3"
|
||||||
agp = "8.3.1"
|
agp = "8.7.0"
|
||||||
coilCompose = "2.6.0"
|
calendar = "2.5.4"
|
||||||
compose = "2.5.0"
|
coilCompose = "2.7.0"
|
||||||
|
compose = "1.6.4" # Updating this will cause an error!
|
||||||
coreSplashscreen = "1.0.1"
|
coreSplashscreen = "1.0.1"
|
||||||
datastorePreferences = "1.0.0"
|
datastorePreferences = "1.1.1"
|
||||||
desugar_jdk_libs = "2.0.4"
|
desugar_jdk_libs = "2.1.2"
|
||||||
kotlin = "1.9.0"
|
kotlin = "1.9.0"
|
||||||
coreKtx = "1.12.0"
|
coreKtx = "1.13.1"
|
||||||
junitVersion = "1.1.5"
|
junitVersion = "1.2.1"
|
||||||
espressoCore = "3.5.1"
|
espressoCore = "3.6.1"
|
||||||
ktorClientCio = "2.3.9"
|
ktor = "2.3.12"
|
||||||
ktorClientCore = "2.3.9"
|
lifecycle = "2.8.5"
|
||||||
ktorClientLogging = "2.3.9"
|
activityCompose = "1.9.2"
|
||||||
lifecycleRuntimeKtx = "2.7.0"
|
composeBom = "2024.03.00" # Updating this will cause an error!
|
||||||
activityCompose = "1.8.2"
|
appcompat = "1.7.0"
|
||||||
composeBom = "2024.02.02"
|
material = "1.12.0"
|
||||||
appcompat = "1.6.1"
|
annotation = "1.8.2"
|
||||||
material = "1.11.0"
|
|
||||||
annotation = "1.7.1"
|
|
||||||
constraintlayout = "2.1.4"
|
constraintlayout = "2.1.4"
|
||||||
lifecycleLivedataKtx = "2.7.0"
|
activity = "1.9.2"
|
||||||
lifecycleViewmodelKtx = "2.7.0"
|
navigationCompose = "2.7.7" # Updating this will cause an error!
|
||||||
activity = "1.8.2"
|
roomRuntime = "2.6.1"
|
||||||
navigationCompose = "2.7.7"
|
|
||||||
richeditorCompose = "1.0.0-rc01"
|
|
||||||
secretsGradlePlugin = "2.0.1"
|
secretsGradlePlugin = "2.0.1"
|
||||||
splittiesFunPackAndroidBaseWithViewsDsl = "3.0.0"
|
splittiesFunPackAndroidBaseWithViewsDsl = "3.0.0"
|
||||||
kefirbb = "1.5"
|
kefirbb = "1.5"
|
||||||
@@ -35,32 +32,35 @@ androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref =
|
|||||||
androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" }
|
androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" }
|
||||||
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
|
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
|
||||||
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
|
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
|
||||||
|
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomRuntime" }
|
||||||
|
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomRuntime" }
|
||||||
|
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" }
|
||||||
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coilCompose" }
|
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coilCompose" }
|
||||||
compose = { module = "com.kizitonwose.calendar:compose", version.ref = "compose" }
|
compose = { module = "com.kizitonwose.calendar:compose", version.ref = "calendar" }
|
||||||
desugar_jdk_libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar_jdk_libs" }
|
desugar_jdk_libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar_jdk_libs" }
|
||||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||||
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||||
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
|
||||||
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
|
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
|
||||||
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
||||||
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
|
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
|
||||||
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
||||||
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
||||||
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
||||||
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest", version.ref = "compose" }
|
||||||
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||||
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||||
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktorClientCio" }
|
ktor-client-android = { module = "io.ktor:ktor-client-android", version.ref = "ktor" }
|
||||||
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktorClientCore" }
|
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" }
|
||||||
ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktorClientLogging" }
|
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
|
||||||
|
ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }
|
||||||
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
||||||
androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "annotation" }
|
androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "annotation" }
|
||||||
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
|
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
|
||||||
androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycleLivedataKtx" }
|
androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycle" }
|
||||||
androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
|
androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle" }
|
||||||
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
|
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
|
||||||
richeditor-compose = { module = "com.mohamedrejeb.richeditor:richeditor-compose", version.ref = "richeditorCompose" }
|
|
||||||
secrets-gradle-plugin = { module = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin", version.ref = "secretsGradlePlugin" }
|
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-funpack-android-base-with-views-dsl = { module = "com.louiscad.splitties:splitties-fun-pack-android-base-with-views-dsl", version.ref = "splittiesFunPackAndroidBaseWithViewsDsl" }
|
||||||
kefirbb = { group = "org.kefirsf", name = "kefirbb", version.ref = "kefirbb" }
|
kefirbb = { group = "org.kefirsf", name = "kefirbb", version.ref = "kefirbb" }
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
#Sat Mar 16 18:30:45 MSK 2024
|
#Sat Mar 16 18:30:45 MSK 2024
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
Reference in new issue
Block a user