commit 16f284b30ac3c51518d856501be8825e3fac710e Author: megboyzz Date: Thu Sep 19 01:24:11 2024 +0300 init commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..0e106ff --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..6806f5a --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,53 @@ + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..6d0ee1c --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..b2c751a --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..931b96c --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..a07f134 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,77 @@ +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.compose) + kotlin("kapt") + id("com.google.dagger.hilt.android") +} + +android { + namespace = "ru.megboyzz.lks.profile" + compileSdk = 34 + + defaultConfig { + applicationId = "ru.megboyzz.lks.profile" + minSdk = 24 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + 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 + } +} + +dependencies { + + 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) + testImplementation(libs.junit) + 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("com.google.dagger:hilt-android:2.51.1") + kapt("com.google.dagger:hilt-android-compiler:2.51.1") + + + implementation(libs.voyager.navigator) + + implementation(libs.voyager.screenmodel) + + implementation(libs.voyager.bottom.sheet.navigator) + + implementation(libs.voyager.transitions) + + implementation(libs.voyager.hilt) + + implementation(libs.jsoup) +} \ No newline at end of file diff --git a/app/data/.gitignore b/app/data/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/data/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/data/build.gradle.kts b/app/data/build.gradle.kts new file mode 100644 index 0000000..87b179c --- /dev/null +++ b/app/data/build.gradle.kts @@ -0,0 +1,45 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) +} + +android { + namespace = "ru.megboyzz.lks.data" + compileSdk = 34 + + defaultConfig { + minSdk = 24 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + 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" + } +} + +dependencies { + + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.material) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + + implementation(libs.jsoup) +} \ No newline at end of file diff --git a/app/data/proguard-rules.pro b/app/data/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/data/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/app/data/src/androidTest/java/ru/megboyzz/lks/data/ExampleInstrumentedTest.kt b/app/data/src/androidTest/java/ru/megboyzz/lks/data/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..85c6c54 --- /dev/null +++ b/app/data/src/androidTest/java/ru/megboyzz/lks/data/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package ru.megboyzz.lks.data + +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.megboyzz.lks.data.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/data/src/main/AndroidManifest.xml b/app/data/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a5918e6 --- /dev/null +++ b/app/data/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/data/src/test/java/ru/megboyzz/lks/data/ExampleUnitTest.kt b/app/data/src/test/java/ru/megboyzz/lks/data/ExampleUnitTest.kt new file mode 100644 index 0000000..f986a9e --- /dev/null +++ b/app/data/src/test/java/ru/megboyzz/lks/data/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package ru.megboyzz.lks.data + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/app/domain/.gitignore b/app/domain/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/domain/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/domain/build.gradle.kts b/app/domain/build.gradle.kts new file mode 100644 index 0000000..83fea22 --- /dev/null +++ b/app/domain/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + id("java-library") + alias(libs.plugins.jetbrains.kotlin.jvm) +} + +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 +} + +dependencies { + + testImplementation(libs.junit) + +} \ No newline at end of file diff --git a/app/domain/src/test/kotlin/EntityTest.kt b/app/domain/src/test/kotlin/EntityTest.kt new file mode 100644 index 0000000..bc33cfa --- /dev/null +++ b/app/domain/src/test/kotlin/EntityTest.kt @@ -0,0 +1,69 @@ +import org.junit.Test +import ru.megboyzz.lks.domain.entities.Student + +class EntityTest { + + @Test + fun testStudent() { + + val student = Student( + surname = "Киц", + name = "Михаил", + patronymic = "Андреевич" + ) + + assert(student.toString() == "Киц Михаил Андреевич") + + } + + @Test + fun testStudent2() { + val student = Student( + surname = null, + name = "Михаил", + patronymic = "Андреевич" + ) + + assert(student.toString() == "Михаил Андреевич") + } + + @Test + fun testStudent3() { + + val student = Student( + surname = "Киц", + name = null, + patronymic = "Андреевич" + ) + println(student) + assert(student.toString() == "Киц Андреевич") + + } + + @Test + fun testStudent4() { + + val student = Student( + surname = "Киц", + name = "Михаил", + patronymic = null + ) + + assert(student.toString() == "Киц Михаил") + + } + + @Test + fun testStudent5() { + + val student = Student( + surname = null, + name = null, + patronymic = null + ) + + assert(student.toString().isEmpty()) + + } + +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/app/src/androidTest/java/ru/megboyzz/lks/profile/ExampleInstrumentedTest.kt b/app/src/androidTest/java/ru/megboyzz/lks/profile/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..d1bde32 --- /dev/null +++ b/app/src/androidTest/java/ru/megboyzz/lks/profile/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package ru.megboyzz.lks.profile + +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.megboyzz.lks.profile", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..31a2708 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/MainActivity.kt b/app/src/main/java/ru/megboyzz/lks/profile/MainActivity.kt new file mode 100644 index 0000000..2b0de39 --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/MainActivity.kt @@ -0,0 +1,26 @@ +package ru.megboyzz.lks.profile + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import cafe.adriel.voyager.navigator.Navigator +import ru.megboyzz.lks.profile.ui.screens.SignInScreen +import ru.megboyzz.lks.profile.ui.theme.MainTheme + +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + setContent { + Navigator(SignInScreen()) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/data/Core.kt b/app/src/main/java/ru/megboyzz/lks/profile/data/Core.kt new file mode 100644 index 0000000..a864d98 --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/data/Core.kt @@ -0,0 +1,94 @@ +package ru.megboyzz.lks.profile.data + +import ru.megboyzz.lks.profile.domain.entities.LksRequest +import ru.megboyzz.lks.profile.domain.entities.LksResponse + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.jsoup.Connection +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import ru.megboyzz.lks.profile.domain.entities.LksStudent + +private typealias Cookies = Map + +private fun Connection.dataMap(vararg pairs: Pair) = this.data(mapOf(*pairs)) + +suspend fun signInLks(username: String, password: String) = withContext(Dispatchers.IO) { + val cookies = Jsoup + .connect("https://lk.mirea.ru/auth.php?login=yes") + .dataMap( + "AUTH_FORM" to "Y", + "TYPE" to "AUTH", + "USER_LOGIN" to username, + "USER_PASSWORD" to password, + "USER_REMEMBER" to "Y" + ) + .header("Content-Type", "multipart/form-data") + .method(Connection.Method.POST) + .ignoreContentType(true) + .execute().cookies() + + if(cookies["PHPSESSID"] == "deleted") + return@withContext cookies as Cookies + else + return@withContext mapOf() +} + +suspend fun fakeConnect(cookies: Cookies) = withContext(Dispatchers.IO) { + Jsoup + .connect("https://lk.mirea.ru/") + .cookies(cookies) + .referrer("https://lk.mirea.ru/") + .execute() +} + +suspend fun getPage(payload: LksRequest, cookies: Cookies, page: Int) = withContext(Dispatchers.IO) { + + val sesid = cookies["BITRIX_SM_UIDH"] + + fakeConnect(cookies) + Jsoup + .connect("https://lk.mirea.ru/staff/students/?${payload.getSubmitUrlPath(sesid ?: "")}&PAGEN_1=$page") + .cookies(cookies) + .referrer("https://lk.mirea.ru/") + .execute().parse() +} + +fun Document.getTotalPages() = kotlin.runCatching { + this.select("body > div.container > div.bx-pagination > div > ul > li:nth-child(6) > a > span").text().toInt() +}.getOrNull() ?: 0 + +fun Document.findStudentsInPage(): LksResponse { + + val table = this.select("body > div.container > table > tbody") ?: return LksResponse() + + val students = table.select("tr").map { + + var name: String? = null + var group: String? = null + var department: String? = null + + it.select("td").forEachIndexed { row, element -> + when(row) { + // Парсим имя + 0 -> name = element.select("td").text() + 1 -> department = element.select("td").text() + 2 -> group = element.select("td").text() + } + } + + LksStudent( + studentFullName = name, + group = group, + department = department + ) + + } + + return LksResponse( + students = students, + availablePages = this.getTotalPages() + ) + +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/data/di/CoroutineModule.kt b/app/src/main/java/ru/megboyzz/lks/profile/data/di/CoroutineModule.kt new file mode 100644 index 0000000..c339d24 --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/data/di/CoroutineModule.kt @@ -0,0 +1,27 @@ +package ru.megboyzz.lks.profile.data.di + +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineName +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.plus +import ru.megboyzz.lks.profile.domain.LocalDispatcherScope +import ru.megboyzz.lks.profile.domain.NetworkDispatcherScope +import kotlin.coroutines.CoroutineContext + +@Module +@InstallIn(SingletonComponent::class) +class CoroutineModule { + + @Provides + @LocalDispatcherScope + fun providesLocalDispatcher() = CoroutineScope(Dispatchers.IO) + CoroutineName("Local") + + @Provides + @NetworkDispatcherScope + fun providesNetworkDispatcher() = CoroutineScope(Dispatchers.IO) + CoroutineName("Net") + +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/data/repositories/LksRepositoryImpl.kt b/app/src/main/java/ru/megboyzz/lks/profile/data/repositories/LksRepositoryImpl.kt new file mode 100644 index 0000000..5abb515 --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/data/repositories/LksRepositoryImpl.kt @@ -0,0 +1,55 @@ +package ru.megboyzz.lks.profile.data.repositories + +import android.content.SharedPreferences +import androidx.core.content.edit +import ru.megboyzz.lks.profile.data.findStudentsInPage +import ru.megboyzz.lks.profile.data.signInLks +import ru.megboyzz.lks.profile.domain.CookiesPrefs +import ru.megboyzz.lks.profile.domain.entities.LksRequest +import ru.megboyzz.lks.profile.domain.entities.LksResponse +import ru.megboyzz.lks.profile.domain.repositories.LksRepository +import javax.inject.Inject + +class LksRepositoryImpl @Inject constructor( + @CookiesPrefs + private val sharedPreferences: SharedPreferences +) : LksRepository { + + private val cookieNamesKey = "cookie_names" + + override suspend fun signInAndGetCookies( + username: String, + password: String + ): Map = signInLks(username, password) + + + override fun saveCookie(cookie: Map) { + sharedPreferences.edit { + cookie.forEach { + this.putString(it.key, it.value) + } + this.putStringSet(cookieNamesKey, cookie.keys) + this.commit() + } + } + + override fun restoreCooke(): Map { + val keys = sharedPreferences.getStringSet(cookieNamesKey, setOf()) + if(keys!!.isEmpty()) return mapOf() + return keys.associateWith { + sharedPreferences.getString(it, "") ?: "" + } + } + + override suspend fun getPage(lksRequest: LksRequest, cookie: Map, pageNumber: Int): Result { + + val document = ru.megboyzz.lks.profile.data.getPage(lksRequest, cookie, pageNumber) + + val page = document.findStudentsInPage() + + return if(page == LksResponse()) + Result.failure(Exception()) + else + Result.success(page) + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/data/repositories/StudentRepositoryImpl.kt b/app/src/main/java/ru/megboyzz/lks/profile/data/repositories/StudentRepositoryImpl.kt new file mode 100644 index 0000000..eeda0b0 --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/data/repositories/StudentRepositoryImpl.kt @@ -0,0 +1,18 @@ +package ru.megboyzz.lks.profile.data.repositories + +import ru.megboyzz.lks.profile.domain.entities.LksResponse +import ru.megboyzz.lks.profile.domain.repositories.StudentRepository + +class StudentRepositoryImpl : StudentRepository { + override fun getAllFavStudents(): List { + TODO("Not yet implemented") + } + + override fun addStudent(lksResponse: LksResponse) { + TODO("Not yet implemented") + } + + override fun deleteFormFav(lksResponse: LksResponse) { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/domain/Scopes.kt b/app/src/main/java/ru/megboyzz/lks/profile/domain/Scopes.kt new file mode 100644 index 0000000..e294916 --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/domain/Scopes.kt @@ -0,0 +1,12 @@ +package ru.megboyzz.lks.profile.domain + +import javax.inject.Scope + +@Scope +annotation class NetworkDispatcherScope + +@Scope +annotation class LocalDispatcherScope + +@Scope +annotation class CookiesPrefs \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/domain/entities/LksRequest.kt b/app/src/main/java/ru/megboyzz/lks/profile/domain/entities/LksRequest.kt new file mode 100644 index 0000000..baf8acc --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/domain/entities/LksRequest.kt @@ -0,0 +1,16 @@ +package ru.megboyzz.lks.profile.domain.entities + +/** + * Класс описывающий запрос/ответ к/от лкс + */ +data class LksRequest( + var student: Student, + var group: String?, + var department: String? +){ + /** + * Метод для получения пути после https://lk.mirea.ru/staff/students? ... + */ + fun getSubmitUrlPath(sesid: String) = + "sessid=$sesid&findname=${student}&findgroup=${group ?: ""}&finddepartment=${department ?: ""}&submit-filter=Найти" +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/domain/entities/LksResponse.kt b/app/src/main/java/ru/megboyzz/lks/profile/domain/entities/LksResponse.kt new file mode 100644 index 0000000..7336ea4 --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/domain/entities/LksResponse.kt @@ -0,0 +1,6 @@ +package ru.megboyzz.lks.profile.domain.entities + +data class LksResponse( + val students: List = emptyList(), + val availablePages: Int = 0 +) \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/domain/entities/LksStudent.kt b/app/src/main/java/ru/megboyzz/lks/profile/domain/entities/LksStudent.kt new file mode 100644 index 0000000..dd21ef9 --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/domain/entities/LksStudent.kt @@ -0,0 +1,7 @@ +package ru.megboyzz.lks.profile.domain.entities + +data class LksStudent( + val studentFullName: String? = null, + val department: String? = null, + val group: String? = null, +) diff --git a/app/src/main/java/ru/megboyzz/lks/profile/domain/entities/Resource.kt b/app/src/main/java/ru/megboyzz/lks/profile/domain/entities/Resource.kt new file mode 100644 index 0000000..3891313 --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/domain/entities/Resource.kt @@ -0,0 +1,10 @@ +package ru.megboyzz.lks.profile.domain.entities + +sealed class Resource { + + data object Idle : Resource() + + data object Loading : Resource() + data class Success(val data: String) : Resource() + data class Error(val message: String) : Resource() +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/domain/entities/Student.kt b/app/src/main/java/ru/megboyzz/lks/profile/domain/entities/Student.kt new file mode 100644 index 0000000..1e379ba --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/domain/entities/Student.kt @@ -0,0 +1,20 @@ +package ru.megboyzz.lks.profile.domain.entities + + +data class Student( + /** + * Имя студента + */ + var name: String?, + /** + * Фамилия Студента + */ + var surname: String?, + + /** + * Отчество студента + */ + var patronymic: String? +) { + override fun toString() = listOfNotNull(surname, name, patronymic).joinToString(" ") +} diff --git a/app/src/main/java/ru/megboyzz/lks/profile/domain/repositories/LksRepository.kt b/app/src/main/java/ru/megboyzz/lks/profile/domain/repositories/LksRepository.kt new file mode 100644 index 0000000..d5b10b9 --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/domain/repositories/LksRepository.kt @@ -0,0 +1,16 @@ +package ru.megboyzz.lks.profile.domain.repositories + +import ru.megboyzz.lks.profile.domain.entities.LksRequest +import ru.megboyzz.lks.profile.domain.entities.LksResponse + +interface LksRepository { + + suspend fun signInAndGetCookies(username: String, password: String): Map + + suspend fun getPage(lksRequest: LksRequest, cookie: Map, pageNumber: Int): Result + + fun saveCookie(cookie: Map) + + fun restoreCooke(): Map + +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/domain/repositories/StudentRepository.kt b/app/src/main/java/ru/megboyzz/lks/profile/domain/repositories/StudentRepository.kt new file mode 100644 index 0000000..ff891c2 --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/domain/repositories/StudentRepository.kt @@ -0,0 +1,13 @@ +package ru.megboyzz.lks.profile.domain.repositories + +import ru.megboyzz.lks.profile.domain.entities.LksResponse + +interface StudentRepository { + + fun getAllFavStudents(): List + + fun addStudent(lksResponse: LksResponse) + + fun deleteFormFav(lksResponse: LksResponse) + +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/SignInUseCase.kt b/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/SignInUseCase.kt new file mode 100644 index 0000000..fd89aea --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/SignInUseCase.kt @@ -0,0 +1,28 @@ +package ru.megboyzz.lks.profile.domain.usecases + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext +import ru.megboyzz.lks.profile.data.signInLks +import ru.megboyzz.lks.profile.domain.NetworkDispatcherScope +import ru.megboyzz.lks.profile.domain.entities.Resource +import ru.megboyzz.lks.profile.domain.repositories.LksRepository +import javax.inject.Inject + +class SignInUseCase @Inject constructor( + private val lksRepository: LksRepository, + @NetworkDispatcherScope + private val coroutineDispatcher: CoroutineDispatcher +) { + + suspend operator fun invoke(username: String, password: String): Resource = withContext(coroutineDispatcher) { + + val cookies = signInLks(username, password) + + return@withContext if(cookies.containsKey("BITRIX_SM_UIDH")) { + lksRepository.saveCookie(cookies) + Resource.Success("SignIn success") + }else + Resource.Error(message = "Wrong creds") + } + +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/fav/AddFavoriteUseCase.kt b/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/fav/AddFavoriteUseCase.kt new file mode 100644 index 0000000..1ba679c --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/fav/AddFavoriteUseCase.kt @@ -0,0 +1,20 @@ +package ru.megboyzz.lks.profile.domain.usecases.fav + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext +import ru.megboyzz.lks.profile.domain.LocalDispatcherScope +import ru.megboyzz.lks.profile.domain.entities.LksResponse +import ru.megboyzz.lks.profile.domain.repositories.StudentRepository +import javax.inject.Inject + +class AddFavoriteUseCase @Inject constructor( + private val studentRepository: StudentRepository, + @LocalDispatcherScope + private val coroutineDispatcher: CoroutineDispatcher +) { + + suspend operator fun invoke(lksResponse: LksResponse) = withContext(coroutineDispatcher) { + studentRepository.addStudent(lksResponse) + } + +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/fav/GetAllFavoritesUseCase.kt b/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/fav/GetAllFavoritesUseCase.kt new file mode 100644 index 0000000..5f4d1aa --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/fav/GetAllFavoritesUseCase.kt @@ -0,0 +1,17 @@ +package ru.megboyzz.lks.profile.domain.usecases.fav + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext +import ru.megboyzz.lks.profile.domain.LocalDispatcherScope +import ru.megboyzz.lks.profile.domain.repositories.StudentRepository +import javax.inject.Inject + +class GetAllFavoritesUseCase @Inject constructor( + private val studentRepository: StudentRepository, + @LocalDispatcherScope + private val coroutineDispatcher: CoroutineDispatcher +) { + + suspend operator fun invoke() = withContext(coroutineDispatcher) { studentRepository.getAllFavStudents() } + +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/fav/RemoveFavoriteUseCase.kt b/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/fav/RemoveFavoriteUseCase.kt new file mode 100644 index 0000000..8effc9f --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/fav/RemoveFavoriteUseCase.kt @@ -0,0 +1,20 @@ +package ru.megboyzz.lks.profile.domain.usecases.fav + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext +import ru.megboyzz.lks.profile.domain.LocalDispatcherScope +import ru.megboyzz.lks.profile.domain.entities.LksResponse +import ru.megboyzz.lks.profile.domain.repositories.StudentRepository +import javax.inject.Inject + +class RemoveFavoriteUseCase @Inject constructor( + private val studentRepository: StudentRepository, + @LocalDispatcherScope + private val coroutineDispatcher: CoroutineDispatcher +) { + + suspend operator fun invoke(lksResponse: LksResponse) = withContext(coroutineDispatcher) { + studentRepository.deleteFormFav(lksResponse) + } + +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/pages/AddLatestDataSearchUseCase.kt b/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/pages/AddLatestDataSearchUseCase.kt new file mode 100644 index 0000000..7c9a77d --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/pages/AddLatestDataSearchUseCase.kt @@ -0,0 +1,4 @@ +package ru.megboyzz.lks.profile.domain.usecases.pages + +class AddLatestDataSearchUseCase { +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/pages/GetDataPage.kt b/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/pages/GetDataPage.kt new file mode 100644 index 0000000..88128e7 --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/pages/GetDataPage.kt @@ -0,0 +1,20 @@ +package ru.megboyzz.lks.profile.domain.usecases.pages + +import kotlinx.coroutines.withContext +import ru.megboyzz.lks.profile.domain.NetworkDispatcherScope +import ru.megboyzz.lks.profile.domain.entities.LksRequest +import ru.megboyzz.lks.profile.domain.repositories.LksRepository +import javax.inject.Inject +import kotlin.coroutines.CoroutineContext + +class GetDataPage @Inject constructor( + private val lksRepository: LksRepository, + @NetworkDispatcherScope + private val coroutineContext: CoroutineContext +) { + + suspend operator fun invoke(lksRequest: LksRequest, pageNumber: Int) = withContext(coroutineContext) { + //lksRepository.getPage(lksRequest, pageNumber) + } + +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/pages/GetLatestDataSearchUseCase.kt b/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/pages/GetLatestDataSearchUseCase.kt new file mode 100644 index 0000000..631dbfa --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/domain/usecases/pages/GetLatestDataSearchUseCase.kt @@ -0,0 +1,4 @@ +package ru.megboyzz.lks.profile.domain.usecases.pages + +class GetLatestDataSearchUseCase { +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/ui/components/SignInComponents.kt b/app/src/main/java/ru/megboyzz/lks/profile/ui/components/SignInComponents.kt new file mode 100644 index 0000000..4079edd --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/ui/components/SignInComponents.kt @@ -0,0 +1,2 @@ +package ru.megboyzz.lks.profile.ui.components + diff --git a/app/src/main/java/ru/megboyzz/lks/profile/ui/model/SearchScreenModel.kt b/app/src/main/java/ru/megboyzz/lks/profile/ui/model/SearchScreenModel.kt new file mode 100644 index 0000000..683e89b --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/ui/model/SearchScreenModel.kt @@ -0,0 +1,9 @@ +package ru.megboyzz.lks.profile.ui.model + +import cafe.adriel.voyager.core.model.ScreenModel + +class SearchScreenModel : ScreenModel { + + + +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/ui/model/SignInScreenModel.kt b/app/src/main/java/ru/megboyzz/lks/profile/ui/model/SignInScreenModel.kt new file mode 100644 index 0000000..116824e --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/ui/model/SignInScreenModel.kt @@ -0,0 +1,76 @@ +package ru.megboyzz.lks.profile.ui.model + +import androidx.compose.runtime.Immutable +import cafe.adriel.voyager.core.model.ScreenModel +import cafe.adriel.voyager.core.model.screenModelScope +import cafe.adriel.voyager.navigator.Navigator +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import ru.megboyzz.lks.profile.domain.repositories.LksRepository +import ru.megboyzz.lks.profile.ui.screens.SearchScreen + +class SignInScreenModel( + private val lksRepository: LksRepository, + private val navigator: Navigator +) : ScreenModel { + + private val _username = MutableStateFlow("") + val username = _username.asStateFlow() + + private val _password = MutableStateFlow("") + val password = _password.asStateFlow() + + sealed class SignInState(val status: String? = null) { + data object Loading: SignInState() + data object Success: SignInState("Успешный вход") + data class Error(val _status: String) : SignInState(_status) + data object Idle: SignInState() + } + + val state = MutableStateFlow(SignInState.Idle) + + private var cookie: Map = mapOf() + + init { + + cookie = lksRepository.restoreCooke() + + if(cookie.isNotEmpty()) { + navigator.push(SearchScreen()) + } + + state.onEach { + if(it == SignInState.Success) + navigator.push(SearchScreen()) + }.launchIn(screenModelScope) + + } + + fun setUsername(username: String) { + _username.value = username + } + + fun setPassword(password: String) { + _password.value = password + } + + fun signIn() { + screenModelScope.launch(Dispatchers.IO) { + state.emit(SignInState.Loading) + cookie = lksRepository.signInAndGetCookies(_username.value, _password.value) + if(cookie.isEmpty()) + state.emit(SignInState.Error("Неправильный логин или пароль")) + else { + state.emit(SignInState.Success) + lksRepository.saveCookie(cookie) + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/ui/screens/SearchScreen.kt b/app/src/main/java/ru/megboyzz/lks/profile/ui/screens/SearchScreen.kt new file mode 100644 index 0000000..3f2f9f8 --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/ui/screens/SearchScreen.kt @@ -0,0 +1,43 @@ +package ru.megboyzz.lks.profile.ui.screens + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.pointer.PointerIcon.Companion.Text +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator + +class SearchScreen : Screen { + @Composable + override fun Content() { + + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Column( + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + + val navigator = LocalNavigator.current!! + + Text("Успех!") + OutlinedButton( + onClick = navigator::popUntilRoot + ) { + Text("Вернуться") + } + + } + } + + } +} diff --git a/app/src/main/java/ru/megboyzz/lks/profile/ui/screens/SignInScreen.kt b/app/src/main/java/ru/megboyzz/lks/profile/ui/screens/SignInScreen.kt new file mode 100644 index 0000000..a238acd --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/ui/screens/SignInScreen.kt @@ -0,0 +1,86 @@ +package ru.megboyzz.lks.profile.ui.screens + +import android.content.Context +import android.content.SharedPreferences +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.core.model.rememberScreenModel +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import ru.megboyzz.lks.profile.data.repositories.LksRepositoryImpl +import ru.megboyzz.lks.profile.ui.model.SignInScreenModel +import kotlin.math.log + +class SignInScreen : Screen { + + @Composable + override fun Content() { + + val context = LocalContext.current + + val navigator = LocalNavigator.current!! + + val viewModel = rememberScreenModel { + val preferences = context.getSharedPreferences("my_prefs", Context.MODE_PRIVATE) + SignInScreenModel( + lksRepository = LksRepositoryImpl(preferences), + navigator = navigator + ) + } + + val username by viewModel.username.collectAsState() + val password by viewModel.password.collectAsState() + + val state by viewModel.state.collectAsState() + + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Column( + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + + OutlinedTextField( + value = username, + onValueChange = { viewModel.setUsername(it) } + ) + + OutlinedTextField( + value = password, + onValueChange = { viewModel.setPassword(it) } + ) + + OutlinedButton( + onClick = viewModel::signIn + ) { + Text("Войти") + } + + when(state) { + is SignInScreenModel.SignInState.Idle -> Text("") + is SignInScreenModel.SignInState.Success -> Text(state.status ?: "") + is SignInScreenModel.SignInState.Loading -> CircularProgressIndicator() + is SignInScreenModel.SignInState.Error -> Text(state.status ?: "Ошибка") + } + + } + } + + } +} diff --git a/app/src/main/java/ru/megboyzz/lks/profile/ui/theme/Color.kt b/app/src/main/java/ru/megboyzz/lks/profile/ui/theme/Color.kt new file mode 100644 index 0000000..dfa5b47 --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/ui/theme/Color.kt @@ -0,0 +1,11 @@ +package ru.megboyzz.lks.profile.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/ui/theme/Theme.kt b/app/src/main/java/ru/megboyzz/lks/profile/ui/theme/Theme.kt new file mode 100644 index 0000000..fa181e4 --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/ui/theme/Theme.kt @@ -0,0 +1,57 @@ +package ru.megboyzz.lks.profile.ui.theme + +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +@Composable +fun MainTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/app/src/main/java/ru/megboyzz/lks/profile/ui/theme/Type.kt b/app/src/main/java/ru/megboyzz/lks/profile/ui/theme/Type.kt new file mode 100644 index 0000000..aa54b05 --- /dev/null +++ b/app/src/main/java/ru/megboyzz/lks/profile/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package ru.megboyzz.lks.profile.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 + ) + */ +) \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..4bbc63d --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + LKS PROFILE + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..6e03abf --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ + + + +