sign in released

This commit is contained in:
2024-09-21 19:38:16 +03:00
parent 16f284b30a
commit 689c23921d
29 changed files with 216 additions and 157 deletions
Generated
+1
View File
@@ -0,0 +1 @@
LKS PROFILE
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="21" />
</component>
</project>
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
</selectionStates>
</component>
</project>
Generated
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
+2 -2
View File
@@ -59,8 +59,8 @@ dependencies {
debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest) debugImplementation(libs.androidx.ui.test.manifest)
implementation("com.google.dagger:hilt-android:2.51.1") implementation(libs.hilt.android)
kapt("com.google.dagger:hilt-android-compiler:2.51.1") kapt(libs.hilt.android.compiler)
implementation(libs.voyager.navigator) implementation(libs.voyager.navigator)
+1
View File
@@ -5,6 +5,7 @@
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<application <application
android:name=".data.App"
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"
@@ -12,9 +12,11 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.Navigator
import dagger.hilt.android.AndroidEntryPoint
import ru.megboyzz.lks.profile.ui.screens.SignInScreen import ru.megboyzz.lks.profile.ui.screens.SignInScreen
import ru.megboyzz.lks.profile.ui.theme.MainTheme import ru.megboyzz.lks.profile.ui.theme.MainTheme
@AndroidEntryPoint
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -0,0 +1,8 @@
package ru.megboyzz.lks.profile.data
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
import dagger.hilt.android.qualifiers.ApplicationContext
@HiltAndroidApp
class App : Application()
@@ -0,0 +1,38 @@
package ru.megboyzz.lks.profile.data.di
import android.content.Context
import android.content.SharedPreferences
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import ru.megboyzz.lks.profile.data.repositories.LksRepositoryImpl
import ru.megboyzz.lks.profile.domain.CookiesPrefs
import ru.megboyzz.lks.profile.domain.DefaultCookies
import ru.megboyzz.lks.profile.domain.repositories.LksRepository
@Module
@InstallIn(SingletonComponent::class)
class DataModule {
@Provides
fun providesLksRepo(
@CookiesPrefs
cookiesPrefs: SharedPreferences
): LksRepository = LksRepositoryImpl(cookiesPrefs)
@Provides
fun providesCookiesPrefs(@ApplicationContext context: Context): SharedPreferences =
context.getSharedPreferences("my_prefs", Context.MODE_PRIVATE)
@Provides
@DefaultCookies
fun providesDefaultCookies() = mapOf(
"PHPSESSID" to "deleted",
"BITRIX_SM_SOUND_LOGIN_PLAYED" to "Y",
"BITRIX_SM_UIDH" to "00000000000000000000000000000000",
"BITRIX_SM_UIDL" to "ivachev.a.a%40edu.mirea.ru"
)
}
@@ -22,6 +22,10 @@ class LksRepositoryImpl @Inject constructor(
password: String password: String
): Map<String, String> = signInLks(username, password) ): Map<String, String> = signInLks(username, password)
override suspend fun cookiesIsValid(cookie: Map<String, String>): Boolean {
return true
}
override fun saveCookie(cookie: Map<String, String>) { override fun saveCookie(cookie: Map<String, String>) {
sharedPreferences.edit { sharedPreferences.edit {
@@ -1,5 +1,6 @@
package ru.megboyzz.lks.profile.domain package ru.megboyzz.lks.profile.domain
import javax.inject.Qualifier
import javax.inject.Scope import javax.inject.Scope
@Scope @Scope
@@ -9,4 +10,7 @@ annotation class NetworkDispatcherScope
annotation class LocalDispatcherScope annotation class LocalDispatcherScope
@Scope @Scope
annotation class CookiesPrefs annotation class CookiesPrefs
@Qualifier
annotation class DefaultCookies
@@ -4,9 +4,9 @@ package ru.megboyzz.lks.profile.domain.entities
* Класс описывающий запрос/ответ к/от лкс * Класс описывающий запрос/ответ к/от лкс
*/ */
data class LksRequest( data class LksRequest(
var student: Student, var student: Student? = null,
var group: String?, var group: String? = null,
var department: String? var department: String? = null
){ ){
/** /**
* Метод для получения пути после https://lk.mirea.ru/staff/students? ... * Метод для получения пути после https://lk.mirea.ru/staff/students? ...
@@ -7,6 +7,8 @@ interface LksRepository {
suspend fun signInAndGetCookies(username: String, password: String): Map<String, String> suspend fun signInAndGetCookies(username: String, password: String): Map<String, String>
suspend fun cookiesIsValid(cookie: Map<String, String>): Boolean
suspend fun getPage(lksRequest: LksRequest, cookie: Map<String, String>, pageNumber: Int): Result<LksResponse> suspend fun getPage(lksRequest: LksRequest, cookie: Map<String, String>, pageNumber: Int): Result<LksResponse>
fun saveCookie(cookie: Map<String, String>) fun saveCookie(cookie: Map<String, String>)
@@ -1,28 +0,0 @@
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")
}
}
@@ -1,20 +0,0 @@
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)
}
}
@@ -1,17 +0,0 @@
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() }
}
@@ -1,20 +0,0 @@
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)
}
}
@@ -1,4 +0,0 @@
package ru.megboyzz.lks.profile.domain.usecases.pages
class AddLatestDataSearchUseCase {
}
@@ -1,20 +0,0 @@
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)
}
}
@@ -1,4 +0,0 @@
package ru.megboyzz.lks.profile.domain.usecases.pages
class GetLatestDataSearchUseCase {
}
@@ -0,0 +1,32 @@
package ru.megboyzz.lks.profile.ui
import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.hilt.ScreenModelFactory
import cafe.adriel.voyager.hilt.ScreenModelFactoryKey
import cafe.adriel.voyager.hilt.ScreenModelKey
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityComponent
import dagger.multibindings.IntoMap
import ru.megboyzz.lks.profile.ui.model.SignInScreenModel
@Module
@InstallIn(ActivityComponent::class)
interface HiltModule {
/*@Binds
@IntoMap
@ScreenModelKey(SignInScreenModel::class)
abstract fun bindSignInScreenModel(
signInScreenModel: SignInScreenModel
) : ScreenModel*/
@Binds
@IntoMap
@ScreenModelFactoryKey(SignInScreenModel.Factory::class)
fun bindSignInScreenModelFactory(
factory: SignInScreenModel.Factory
): ScreenModelFactory
}
@@ -0,0 +1,14 @@
package ru.megboyzz.lks.profile.ui.components
import android.provider.ContactsContract.Profile
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import ru.megboyzz.lks.profile.domain.entities.LksResponse
import ru.megboyzz.lks.profile.domain.entities.LksStudent
@Composable
fun ProfileCard(lksStudent: LksStudent) {
}
@@ -1,2 +0,0 @@
package ru.megboyzz.lks.profile.ui.components
@@ -1,8 +1,55 @@
package ru.megboyzz.lks.profile.ui.model package ru.megboyzz.lks.profile.ui.model
import cafe.adriel.voyager.core.model.ScreenModel import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import ru.megboyzz.lks.profile.domain.DefaultCookies
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 SearchScreenModel : ScreenModel { class SearchScreenModel @Inject constructor(
private val lksRepository: LksRepository,
@DefaultCookies
private val cookies: Map<String, String>
) : ScreenModel {
private val _request = MutableStateFlow(LksRequest())
val request = _request.asStateFlow()
private val _curPageNumber = MutableStateFlow(1)
val curPageNumber = _curPageNumber.asStateFlow()
private val _responseList = MutableStateFlow(LksResponse())
val responseList = _responseList.asStateFlow()
init {
_curPageNumber.onEach {
update()
}
}
fun nextPage() = screenModelScope.launch { _curPageNumber.emit(_curPageNumber.value++) }
private fun update() = screenModelScope.launch {
val result = lksRepository.getPage(_request.value, cookies, curPageNumber.value)
val list = result.getOrElse { LksResponse() }
_responseList.emit(list)
}
fun newSearch() = screenModelScope.launch {
_curPageNumber.emit(1)
update()
}
@@ -1,23 +1,25 @@
package ru.megboyzz.lks.profile.ui.model 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.ScreenModel
import cafe.adriel.voyager.core.model.screenModelScope import cafe.adriel.voyager.core.model.screenModelScope
import cafe.adriel.voyager.hilt.ScreenModelFactory
import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.Navigator
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import ru.megboyzz.lks.profile.domain.repositories.LksRepository import ru.megboyzz.lks.profile.domain.repositories.LksRepository
import ru.megboyzz.lks.profile.ui.screens.SearchScreen import ru.megboyzz.lks.profile.ui.screens.SearchScreen
class SignInScreenModel(
class SignInScreenModel @AssistedInject constructor(
private val lksRepository: LksRepository, private val lksRepository: LksRepository,
private val navigator: Navigator @Assisted private val navigator: Navigator
) : ScreenModel { ) : ScreenModel {
private val _username = MutableStateFlow("") private val _username = MutableStateFlow("")
@@ -39,15 +41,14 @@ class SignInScreenModel(
init { init {
cookie = lksRepository.restoreCooke() screenModelScope.launch {
cookie = lksRepository.restoreCooke()
if(cookie.isNotEmpty()) { if(lksRepository.cookiesIsValid(cookie))
navigator.push(SearchScreen()) navigator.push(SearchScreen())
} }
state.onEach { state.onEach {
if(it == SignInState.Success) if(it == SignInState.Success) navigator.push(SearchScreen())
navigator.push(SearchScreen())
}.launchIn(screenModelScope) }.launchIn(screenModelScope)
} }
@@ -73,4 +74,9 @@ class SignInScreenModel(
} }
} }
@AssistedFactory
interface Factory : ScreenModelFactory {
fun create(navigator: Navigator): SignInScreenModel
}
} }
@@ -3,7 +3,9 @@ package ru.megboyzz.lks.profile.ui.screens
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -13,28 +15,28 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.PointerIcon.Companion.Text import androidx.compose.ui.input.pointer.PointerIcon.Companion.Text
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.hilt.getScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import ru.megboyzz.lks.profile.ui.model.SearchScreenModel
class SearchScreen : Screen { class SearchScreen : Screen {
@Composable @Composable
override fun Content() { override fun Content() {
val screenModel = getScreenModel<SearchScreenModel>()
Box( Box(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center contentAlignment = Alignment.TopCenter
) { ) {
Column( Column(
verticalArrangement = Arrangement.spacedBy(16.dp) verticalArrangement = Arrangement.spacedBy(16.dp)
) { ) {
Spacer(Modifier.height(20.dp))
val navigator = LocalNavigator.current!! val navigator = LocalNavigator.current!!
Text("Успех!")
OutlinedButton(
onClick = navigator::popUntilRoot
) {
Text("Вернуться")
}
} }
} }
@@ -1,7 +1,5 @@
package ru.megboyzz.lks.profile.ui.screens 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.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -13,33 +11,23 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.hilt.getScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.LocalNavigator
import ru.megboyzz.lks.profile.data.repositories.LksRepositoryImpl
import ru.megboyzz.lks.profile.ui.model.SignInScreenModel import ru.megboyzz.lks.profile.ui.model.SignInScreenModel
import kotlin.math.log
class SignInScreen : Screen { class SignInScreen : Screen {
@Composable @Composable
override fun Content() { override fun Content() {
val context = LocalContext.current
val navigator = LocalNavigator.current!! val navigator = LocalNavigator.current!!
val viewModel = rememberScreenModel { val viewModel = getScreenModel<SignInScreenModel, SignInScreenModel.Factory> { assistedFactory ->
val preferences = context.getSharedPreferences("my_prefs", Context.MODE_PRIVATE) assistedFactory.create(navigator)
SignInScreenModel(
lksRepository = LksRepositoryImpl(preferences),
navigator = navigator
)
} }
val username by viewModel.username.collectAsState() val username by viewModel.username.collectAsState()
+1 -1
View File
@@ -5,5 +5,5 @@ plugins {
alias(libs.plugins.kotlin.compose) apply false alias(libs.plugins.kotlin.compose) apply false
alias(libs.plugins.jetbrains.kotlin.jvm) apply false alias(libs.plugins.jetbrains.kotlin.jvm) apply false
alias(libs.plugins.android.library) apply false alias(libs.plugins.android.library) apply false
id("com.google.dagger.hilt.android") version "2.44" apply false id("com.google.dagger.hilt.android") version "2.51.1" apply false
} }
+3
View File
@@ -1,5 +1,6 @@
[versions] [versions]
agp = "8.5.1" agp = "8.5.1"
hiltAndroidCompiler = "2.51.1"
jsoup = "1.18.1" jsoup = "1.18.1"
kotlin = "2.0.0" kotlin = "2.0.0"
coreKtx = "1.13.1" coreKtx = "1.13.1"
@@ -16,6 +17,8 @@ voyagerVersion = "1.1.0-beta02"
[libraries] [libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroidCompiler" }
hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltAndroidCompiler" }
jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" }
junit = { group = "junit", name = "junit", version.ref = "junit" } junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }