Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9bdb03d5e | ||
|
|
50f4102b93 | ||
|
|
62b7658fb9 | ||
|
|
193ad2d07a | ||
|
|
7d313e1c64 | ||
|
|
11cafec847 | ||
|
|
589464e69c | ||
|
|
e8ed594b9f | ||
|
|
e32b4feeef | ||
|
|
7d3d3517c5 |
@@ -1,3 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="AndroidProjectSystem">
|
||||||
|
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -5,6 +5,9 @@
|
|||||||
<SelectionState runConfigName="app">
|
<SelectionState runConfigName="app">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
</SelectionState>
|
</SelectionState>
|
||||||
|
<SelectionState runConfigName="DropdownSearchPrev">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
</SelectionState>
|
||||||
</selectionStates>
|
</selectionStates>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -4,14 +4,13 @@
|
|||||||
<component name="GradleSettings">
|
<component name="GradleSettings">
|
||||||
<option name="linkedExternalProjectsSettings">
|
<option name="linkedExternalProjectsSettings">
|
||||||
<GradleProjectSettings>
|
<GradleProjectSettings>
|
||||||
|
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||||
<option name="modules">
|
<option name="modules">
|
||||||
<set>
|
<set>
|
||||||
<option value="$PROJECT_DIR$" />
|
<option value="$PROJECT_DIR$" />
|
||||||
<option value="$PROJECT_DIR$/app" />
|
<option value="$PROJECT_DIR$/app" />
|
||||||
<option value="$PROJECT_DIR$/app/data" />
|
|
||||||
<option value="$PROJECT_DIR$/app/domain" />
|
|
||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
<option name="resolveExternalAnnotations" value="false" />
|
<option name="resolveExternalAnnotations" value="false" />
|
||||||
|
|||||||
@@ -37,6 +37,10 @@
|
|||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
<option name="previewFile" value="true" />
|
<option name="previewFile" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewDeviceShouldUseNewSpec" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
|
<inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
<option name="previewFile" value="true" />
|
<option name="previewFile" value="true" />
|
||||||
@@ -45,6 +49,10 @@
|
|||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
<option name="previewFile" value="true" />
|
<option name="previewFile" value="true" />
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
|
<inspection_tool class="PreviewParameterProviderOnFirstParameter" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
|
<option name="composableFile" value="true" />
|
||||||
|
<option name="previewFile" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
<inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
|
||||||
<option name="composableFile" value="true" />
|
<option name="composableFile" value="true" />
|
||||||
<option name="previewFile" value="true" />
|
<option name="previewFile" value="true" />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="corretto-21" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|||||||
@@ -5,8 +5,12 @@
|
|||||||
<set>
|
<set>
|
||||||
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||||
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||||
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||||
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||||
|
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
<mapping directory="" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
kotlin version: 2.0.0
|
||||||
|
error message: org.jetbrains.kotlin.util.FileAnalysisException: While analysing /home/megboyzz/workdir/Projects/AndroidStudioProjects/LKSPROFILE/app/src/main/java/ru/megboyzz/lks/profile/ui/components/MainNavigation.kt:34:1: java.lang.IllegalStateException: Zero or multiple overrides found for serialize in FirRegularClassSymbol ru/megboyzz/lks/profile/ui/components/SearchScreen
|
||||||
|
at org.jetbrains.kotlin.util.AnalysisExceptionsKt.wrapIntoFileAnalysisExceptionIfNeeded(AnalysisExceptions.kt:56)
|
||||||
|
at org.jetbrains.kotlin.fir.FirCliExceptionHandler.handleExceptionOnFileAnalysis(Utils.kt:180)
|
||||||
|
at org.jetbrains.kotlin.fir.pipeline.AnalyseKt.runCheckers(analyse.kt:46)
|
||||||
|
at org.jetbrains.kotlin.fir.pipeline.ConvertToIrKt.runPlatformCheckers(convertToIr.kt:62)
|
||||||
|
at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.compileModuleToAnalyzedFir(jvmCompilerPipeline.kt:316)
|
||||||
|
at org.jetbrains.kotlin.cli.jvm.compiler.pipeline.JvmCompilerPipelineKt.compileModulesUsingFrontendIrAndLightTree(jvmCompilerPipeline.kt:116)
|
||||||
|
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:155)
|
||||||
|
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:50)
|
||||||
|
at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:104)
|
||||||
|
at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:48)
|
||||||
|
at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:101)
|
||||||
|
at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:453)
|
||||||
|
at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:62)
|
||||||
|
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.doCompile(IncrementalCompilerRunner.kt:506)
|
||||||
|
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:423)
|
||||||
|
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileNonIncrementally(IncrementalCompilerRunner.kt:301)
|
||||||
|
at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:129)
|
||||||
|
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:676)
|
||||||
|
at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:92)
|
||||||
|
at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1661)
|
||||||
|
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
|
||||||
|
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
|
||||||
|
at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(Unknown Source)
|
||||||
|
at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source)
|
||||||
|
at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source)
|
||||||
|
at java.base/java.security.AccessController.doPrivileged(Unknown Source)
|
||||||
|
at java.rmi/sun.rmi.transport.Transport.serviceCall(Unknown Source)
|
||||||
|
at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source)
|
||||||
|
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source)
|
||||||
|
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source)
|
||||||
|
at java.base/java.security.AccessController.doPrivileged(Unknown Source)
|
||||||
|
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source)
|
||||||
|
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
|
||||||
|
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
|
||||||
|
at java.base/java.lang.Thread.run(Unknown Source)
|
||||||
|
Caused by: java.lang.IllegalStateException: Zero or multiple overrides found for serialize in FirRegularClassSymbol ru/megboyzz/lks/profile/ui/components/SearchScreen
|
||||||
|
at org.jetbrains.kotlinx.serialization.compiler.fir.SerializationFirResolveExtension.getFromSupertype(SerializationFirResolveExtension.kt:146)
|
||||||
|
at org.jetbrains.kotlinx.serialization.compiler.fir.SerializationFirResolveExtension.generateFunctions(SerializationFirResolveExtension.kt:177)
|
||||||
|
at org.jetbrains.kotlin.fir.scopes.impl.FirGeneratedMemberDeclarationsStorage$CallableStorage.generateMemberFunctions(FirGeneratedScopes.kt:198)
|
||||||
|
at org.jetbrains.kotlin.fir.scopes.impl.FirGeneratedMemberDeclarationsStorage$CallableStorage.access$generateMemberFunctions(FirGeneratedScopes.kt:175)
|
||||||
|
at org.jetbrains.kotlin.fir.scopes.impl.FirGeneratedMemberDeclarationsStorage$CallableStorage$special$$inlined$createCache$1.invoke(FirCachesFactory.kt:71)
|
||||||
|
at org.jetbrains.kotlin.fir.scopes.impl.FirGeneratedMemberDeclarationsStorage$CallableStorage$special$$inlined$createCache$1.invoke(FirCachesFactory.kt:69)
|
||||||
|
at org.jetbrains.kotlin.fir.caches.FirThreadUnsafeCache.getValue(FirThreadUnsafeCachesFactory.kt:40)
|
||||||
|
at org.jetbrains.kotlin.fir.scopes.impl.FirGeneratedClassDeclaredMemberScope.processFunctionsByName(FirGeneratedScopes.kt:281)
|
||||||
|
at org.jetbrains.kotlin.fir.scopes.FirCompositeScope.processFunctionsByName(FirCompositeScope.kt:55)
|
||||||
|
at org.jetbrains.kotlin.fir.scopes.FirNameAwareCompositeScope.processFunctionsByName(FirCompositeScope.kt:82)
|
||||||
|
at org.jetbrains.kotlin.fir.scopes.impl.AbstractFirUseSiteMemberScope.collectDeclaredFunctions(AbstractFirUseSiteMemberScope.kt:78)
|
||||||
|
at org.jetbrains.kotlin.fir.scopes.impl.AbstractFirUseSiteMemberScope.collectFunctions(AbstractFirUseSiteMemberScope.kt:72)
|
||||||
|
at org.jetbrains.kotlin.fir.scopes.impl.AbstractFirUseSiteMemberScope.processFunctionsByName(AbstractFirUseSiteMemberScope.kt:63)
|
||||||
|
at org.jetbrains.kotlin.fir.scopes.FirContainingNamesAwareScopeKt.processAllFunctions(FirContainingNamesAwareScope.kt:19)
|
||||||
|
at org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirMultipleDefaultsInheritedFromSupertypesChecker.check(FirMultipleDefaultsInheritedFromSupertypesChecker.kt:44)
|
||||||
|
at org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirMultipleDefaultsInheritedFromSupertypesChecker$Regular.check(FirMultipleDefaultsInheritedFromSupertypesChecker.kt:32)
|
||||||
|
at org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirMultipleDefaultsInheritedFromSupertypesChecker$Regular.check(FirMultipleDefaultsInheritedFromSupertypesChecker.kt:29)
|
||||||
|
at org.jetbrains.kotlin.fir.analysis.collectors.components.DeclarationCheckersDiagnosticComponent.check(DeclarationCheckersDiagnosticComponent.kt:133)
|
||||||
|
at org.jetbrains.kotlin.fir.analysis.collectors.components.DeclarationCheckersDiagnosticComponent.visitRegularClass(DeclarationCheckersDiagnosticComponent.kt:57)
|
||||||
|
at org.jetbrains.kotlin.fir.analysis.collectors.components.DeclarationCheckersDiagnosticComponent.visitRegularClass(DeclarationCheckersDiagnosticComponent.kt:19)
|
||||||
|
at org.jetbrains.kotlin.fir.declarations.FirRegularClass.accept(FirRegularClass.kt:48)
|
||||||
|
at org.jetbrains.kotlin.fir.analysis.collectors.CheckerRunningDiagnosticCollectorVisitor.checkElement(CheckerRunningDiagnosticCollectorVisitor.kt:24)
|
||||||
|
at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.access$checkElement(AbstractDiagnosticCollectorVisitor.kt:29)
|
||||||
|
at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitWithDeclarationAndReceiver(AbstractDiagnosticCollectorVisitor.kt:1349)
|
||||||
|
at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitClassAndChildren(AbstractDiagnosticCollectorVisitor.kt:86)
|
||||||
|
at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitRegularClass(AbstractDiagnosticCollectorVisitor.kt:91)
|
||||||
|
at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitRegularClass(AbstractDiagnosticCollectorVisitor.kt:29)
|
||||||
|
at org.jetbrains.kotlin.fir.declarations.FirRegularClass.accept(FirRegularClass.kt:48)
|
||||||
|
at org.jetbrains.kotlin.fir.declarations.impl.FirFileImpl.acceptChildren(FirFileImpl.kt:56)
|
||||||
|
at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitNestedElements(AbstractDiagnosticCollectorVisitor.kt:37)
|
||||||
|
at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitFile(AbstractDiagnosticCollectorVisitor.kt:1124)
|
||||||
|
at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollectorVisitor.visitFile(AbstractDiagnosticCollectorVisitor.kt:29)
|
||||||
|
at org.jetbrains.kotlin.fir.declarations.FirFile.accept(FirFile.kt:42)
|
||||||
|
at org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector.collectDiagnostics(AbstractDiagnosticCollector.kt:36)
|
||||||
|
at org.jetbrains.kotlin.fir.pipeline.AnalyseKt.runCheckers(analyse.kt:34)
|
||||||
|
... 32 more
|
||||||
|
|
||||||
|
|
||||||
@@ -2,20 +2,21 @@ plugins {
|
|||||||
alias(libs.plugins.android.application)
|
alias(libs.plugins.android.application)
|
||||||
alias(libs.plugins.kotlin.android)
|
alias(libs.plugins.kotlin.android)
|
||||||
alias(libs.plugins.kotlin.compose)
|
alias(libs.plugins.kotlin.compose)
|
||||||
kotlin("kapt")
|
alias(libs.plugins.kotlin.serialization)
|
||||||
|
id("com.google.devtools.ksp")
|
||||||
id("com.google.dagger.hilt.android")
|
id("com.google.dagger.hilt.android")
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "ru.megboyzz.lks.profile"
|
namespace = "ru.megboyzz.lks.profile"
|
||||||
compileSdk = 34
|
compileSdk = 36
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "ru.megboyzz.lks.profile"
|
applicationId = "ru.megboyzz.lks.profile"
|
||||||
minSdk = 24
|
minSdk = 29
|
||||||
targetSdk = 34
|
targetSdk = 36
|
||||||
versionCode = 1
|
versionCode = 105
|
||||||
versionName = "1.0"
|
versionName = "1.0.5"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
@@ -51,6 +52,7 @@ dependencies {
|
|||||||
implementation(libs.androidx.ui.graphics)
|
implementation(libs.androidx.ui.graphics)
|
||||||
implementation(libs.androidx.ui.tooling.preview)
|
implementation(libs.androidx.ui.tooling.preview)
|
||||||
implementation(libs.androidx.material3)
|
implementation(libs.androidx.material3)
|
||||||
|
implementation(libs.androidx.foundation.layout.android)
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(libs.androidx.junit)
|
androidTestImplementation(libs.androidx.junit)
|
||||||
androidTestImplementation(libs.androidx.espresso.core)
|
androidTestImplementation(libs.androidx.espresso.core)
|
||||||
@@ -59,19 +61,14 @@ dependencies {
|
|||||||
debugImplementation(libs.androidx.ui.tooling)
|
debugImplementation(libs.androidx.ui.tooling)
|
||||||
debugImplementation(libs.androidx.ui.test.manifest)
|
debugImplementation(libs.androidx.ui.test.manifest)
|
||||||
|
|
||||||
|
implementation(libs.androidx.room.ktx)
|
||||||
|
implementation(libs.androidx.room.runtime)
|
||||||
|
|
||||||
|
implementation(libs.navigation.compose)
|
||||||
implementation(libs.hilt.android)
|
implementation(libs.hilt.android)
|
||||||
kapt(libs.hilt.android.compiler)
|
ksp(libs.hilt.android.compiler)
|
||||||
|
ksp(libs.androidx.room.compiler)
|
||||||
|
|
||||||
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)
|
implementation(libs.jsoup)
|
||||||
|
implementation(libs.kotlinx.serialization.json)
|
||||||
}
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
/build
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# 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
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
</manifest>
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
/build
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id("java-library")
|
|
||||||
alias(libs.plugins.jetbrains.kotlin.jvm)
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_21
|
|
||||||
targetCompatibility = JavaVersion.VERSION_21
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
|
|
||||||
testImplementation(libs.junit)
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
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())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
{
|
||||||
|
"ИДО": "Институт дополнительного образования",
|
||||||
|
"ИМО": "Институт международного образования",
|
||||||
|
"ИДП": "Институт довузовской подготовки",
|
||||||
|
"ИТХТ": "Институт тонких химических технологий имени М.В. Ломоносова",
|
||||||
|
"ИИИ": "Институт искусственного интеллекта",
|
||||||
|
"БК250": "Базовая кафедра № 250 - математического обеспечения информационных систем",
|
||||||
|
"ИПТ": "Институт перспективных технологий и индустриального программирования",
|
||||||
|
"БК235": "Базовая кафедра № 235 - цифровых устройств и систем защиты информации",
|
||||||
|
"КСТУ": "Кафедра современных технологий управления",
|
||||||
|
"БК536": "Базовая кафедра № 536 - программного обеспечения систем РЭА",
|
||||||
|
"ИКДТ": "Институт кибербезопасности и цифровых технологий",
|
||||||
|
"УПО": "Управление платных образовательных услуг",
|
||||||
|
"КВМ3": "Кафедра высшей математики-3",
|
||||||
|
"КФТМ": "Кафедра физики и технической механики",
|
||||||
|
"УОРТ": "Учебный отдел Института радиотехнических и телекоммуникационных систем",
|
||||||
|
"БК333": "Базовая кафедра № 333 имени профессора Лобанова Б.С. - систем радиоэлектронной борьбы",
|
||||||
|
"БК334": "Базовая кафедра № 334 - дальней радиосвязи",
|
||||||
|
"БК338": "Базовая кафедра № 338 - космической радиоэлектроники",
|
||||||
|
"КИЭТ": "Кафедра инженерной экологии техносферы",
|
||||||
|
"КИЯ1": "Кафедра иностранных языков 1",
|
||||||
|
"КРЭСК": "Кафедра радиоэлектронных систем и комплексов",
|
||||||
|
"КБПФ": "Кафедра биотехнологии и промышленной фармации",
|
||||||
|
"КДГП": "Кафедра документоведения, истории государства и права",
|
||||||
|
"БК340": "Базовая кафедра № 340 - информационных космических систем",
|
||||||
|
"БК343": "Базовая кафедра № 343 - моделирования радиофизических процессов",
|
||||||
|
"ЦДО": "Центр дистанционного обучения",
|
||||||
|
"КФТМ2024": "Кафедра физики и технической механики (от 19.06.2024 № 26-ст)",
|
||||||
|
"КИТГУ": "Кафедра информационных технологий в государственном управлении",
|
||||||
|
"КМОСИТ": "Кафедра математического обеспечения и стандартизации информационных технологий",
|
||||||
|
"КПК": "Колледж программирования и кибербезопасности",
|
||||||
|
"КВТ": "Кафедра вычислительной техники",
|
||||||
|
"ИИТ": "Институт информационных технологий",
|
||||||
|
"КИГ": "Кафедра инженерной графики",
|
||||||
|
"КЭТ": "Кафедра экономической теории",
|
||||||
|
"КОИ": "Кафедра общей информатики",
|
||||||
|
"ККПРЭС": "Кафедра конструирования и производства радиоэлектронных средств",
|
||||||
|
"КСИ": "Кафедра системной инженерии",
|
||||||
|
"Аспирантура": "Аспирантура",
|
||||||
|
"ПОДГ": "Подготовительное отделение для иностранных граждан",
|
||||||
|
"Ректорат": "Ректорат",
|
||||||
|
"ИРИ": "Институт радиоэлектроники и информатики",
|
||||||
|
"ЮО": "Юридический отдел",
|
||||||
|
"УМУ": "Учебно-методическое управление",
|
||||||
|
"УК": "Управление кадров",
|
||||||
|
"ФФГБОУВОС": "Филиал федерального государственного бюджетного образовательного учреждения высшего образования «МИРЭА - Российский технологический университет» в г. Ставрополе",
|
||||||
|
"УГОЧС": "Управление гражданской обороны и чрезвычайных ситуаций",
|
||||||
|
"КН": "Кафедра наноэлектроники",
|
||||||
|
"Бухгалтерия": "Бухгалтерия",
|
||||||
|
"УОИМО": "Учебный отдел института международного образования",
|
||||||
|
"СПВ86": "Сектор «Проспект Вернадского, 86»",
|
||||||
|
"СИАО": "Студенческий информационно-архивный отдел",
|
||||||
|
"УЭФ": "Управление экономики и финансов",
|
||||||
|
"УМС": "Управление международных связей",
|
||||||
|
"УВСР": "Управление по воспитательной и социальной работе",
|
||||||
|
"ПР": "Первый отдел",
|
||||||
|
"СГИ": "Служба главного инженера",
|
||||||
|
"НТБ": "Научно-техническая библиотека",
|
||||||
|
"УИ": "Управление информатизации",
|
||||||
|
"КМС": "Кафедра метрологии и стандартизации",
|
||||||
|
"ССт20": "Сектор «Стромынка, 20»",
|
||||||
|
"ИАО": "Информационно-аналитический отдел",
|
||||||
|
"ОСПР": "Отдел судебно-претензионной работы",
|
||||||
|
"БК140": "Базовая кафедра № 140 - фотоники",
|
||||||
|
"УД": "Управление делами",
|
||||||
|
"УМП": "Управление мобилизационной подготовки",
|
||||||
|
"БК130": "Базовая кафедра № 130 - твердотельной электроники",
|
||||||
|
"НОЦТЦ": "Научно-образовательный центр «Технологический центр»",
|
||||||
|
"БК131": "Базовая кафедра № 131 - оптико-физических измерений",
|
||||||
|
"БК132": "Базовая кафедра № 132 - лазерной техники",
|
||||||
|
"БК136": "Базовая кафедра № 136 - СВЧ приборов и устройств",
|
||||||
|
"ФМШ": "Физико-математическая школа",
|
||||||
|
"КПВВ": "Курсы по подготовке к поступлению в вуз",
|
||||||
|
"ОМКО": "Отдел менеджмента качества образования",
|
||||||
|
"ОТСО": "Отдел технических средств обучения",
|
||||||
|
"ОЗЗИ": "Отдел обеспечения защиты информации",
|
||||||
|
"ОТК": "Отдел технического контроля",
|
||||||
|
"Автобаза": "Автобаза",
|
||||||
|
"Академия ТВ": "Академия ТВ",
|
||||||
|
"ГИВЦ": "Главный информационно-вычислительный центр",
|
||||||
|
"УНИР": "Управление научно-исследовательских разработок",
|
||||||
|
"КДЦ": "Культурно-досуговый центр",
|
||||||
|
"МНТЦИС": "Межотраслевой научно-технический центр интеллектуальных технологий обработки информации и управления «Интеллектуальные системы»",
|
||||||
|
"НТЦН": "Научно-технологический центр «Наука»",
|
||||||
|
"НИЦРСВТ": "Научно-исследовательский центр разработки специальной вычислительной техники",
|
||||||
|
"НИЦИПМ": "Научно-исследовательский центр «Инновационные полимерные материалы и изделия»",
|
||||||
|
"ОИТОСБ": "Отдел инженерно-технического обеспечения средств безопасности",
|
||||||
|
"ЦПИС": "Центр проектирования интегральных схем, устройств наноэлектроники и микросистем",
|
||||||
|
"ЦЯП": "Центр языковой подготовки",
|
||||||
|
"ИМПМО": "Институт молодежной политики и международных отношений",
|
||||||
|
"НИИМФП": "Научно-исследовательский институт медицинской и фармацевтической промышленности",
|
||||||
|
"ИПЦСНК": "Инженерно-научный производственный центр «Средства неразрушающего контроля» МИРЭА",
|
||||||
|
"УЦВ": "Учебный центр подготовки водителей",
|
||||||
|
"ИТП": "Институт технологического предпринимательства",
|
||||||
|
"СУЛСДФ": "Специализированная учебно-научная лаборатория сверхбыстрой динамики ферроиков",
|
||||||
|
"УНИИСУ": "Учебно-научный институт информатики и систем управления",
|
||||||
|
"УМЦИТ": "Учебно-методический центр интерактивных технологий",
|
||||||
|
"ОКБМИРЭА": "Особое конструкторское бюро МИРЭА",
|
||||||
|
"ИУС": "Институт учета и статистики",
|
||||||
|
"НИЦПНТП": "Научно-инжиниринговый центр «Перспективные научно-технические проекты»",
|
||||||
|
"Наноцентр МИРЭА": "Наноцентр МИРЭА",
|
||||||
|
"НТЦИРФ": "Научно-технологический центр «Интегральная радиофотоника»",
|
||||||
|
"РИО": "Редакционно-издательский отдел",
|
||||||
|
"ПЛМА": "Проблемная лаборатория молекулярной активации"
|
||||||
|
}
|
||||||
@@ -4,25 +4,19 @@ import android.os.Bundle
|
|||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
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 dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import ru.megboyzz.lks.profile.ui.screens.SignInScreen
|
import ru.megboyzz.lks.profile.ui.components.MainNavHost
|
||||||
import ru.megboyzz.lks.profile.ui.theme.MainTheme
|
import ru.megboyzz.lks.profile.ui.provider.ActivityProvider
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
Navigator(SignInScreen())
|
MainNavHost()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
package ru.megboyzz.lks.profile.data
|
package ru.megboyzz.lks.profile.data
|
||||||
|
|
||||||
import ru.megboyzz.lks.profile.domain.entities.LksRequest
|
import ru.megboyzz.lks.profile.domain.entities.LksRequest
|
||||||
import ru.megboyzz.lks.profile.domain.entities.LksResponse
|
import ru.megboyzz.lks.profile.domain.entities.LksStudent
|
||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.jsoup.Connection
|
import org.jsoup.Connection
|
||||||
import org.jsoup.Jsoup
|
import org.jsoup.Jsoup
|
||||||
import org.jsoup.nodes.Document
|
import org.jsoup.nodes.Document
|
||||||
import ru.megboyzz.lks.profile.domain.entities.LksStudent
|
import ru.megboyzz.lks.profile.domain.entities.LksResponse
|
||||||
|
|
||||||
private typealias Cookies = Map<String, String>
|
private typealias Cookies = Map<String, String>
|
||||||
|
|
||||||
@@ -17,6 +17,7 @@ private fun Connection.dataMap(vararg pairs: Pair<String, String>) = this.data(m
|
|||||||
suspend fun signInLks(username: String, password: String) = withContext(Dispatchers.IO) {
|
suspend fun signInLks(username: String, password: String) = withContext(Dispatchers.IO) {
|
||||||
val cookies = Jsoup
|
val cookies = Jsoup
|
||||||
.connect("https://lk.mirea.ru/auth.php?login=yes")
|
.connect("https://lk.mirea.ru/auth.php?login=yes")
|
||||||
|
//.userAgent("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36")
|
||||||
.dataMap(
|
.dataMap(
|
||||||
"AUTH_FORM" to "Y",
|
"AUTH_FORM" to "Y",
|
||||||
"TYPE" to "AUTH",
|
"TYPE" to "AUTH",
|
||||||
@@ -35,33 +36,39 @@ suspend fun signInLks(username: String, password: String) = withContext(Dispatch
|
|||||||
return@withContext mapOf<String, String>()
|
return@withContext mapOf<String, String>()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun fakeConnect(cookies: Cookies) = withContext(Dispatchers.IO) {
|
fun fakeConnect(cookies: Cookies) {
|
||||||
Jsoup
|
Jsoup
|
||||||
.connect("https://lk.mirea.ru/")
|
.connect("https://lk.mirea.ru/")
|
||||||
|
//.userAgent("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36")
|
||||||
.cookies(cookies)
|
.cookies(cookies)
|
||||||
.referrer("https://lk.mirea.ru/")
|
.referrer("https://lk.mirea.ru/")
|
||||||
.execute()
|
.execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getPage(payload: LksRequest, cookies: Cookies, page: Int) = withContext(Dispatchers.IO) {
|
fun getProfilePage(payload: LksRequest, cookies: Cookies, page: Int): Document {
|
||||||
|
|
||||||
val sesid = cookies["BITRIX_SM_UIDH"]
|
val sesid = cookies["BITRIX_SM_UIDH"]
|
||||||
|
|
||||||
fakeConnect(cookies)
|
fakeConnect(cookies)
|
||||||
Jsoup
|
|
||||||
|
val conn = Jsoup
|
||||||
.connect("https://lk.mirea.ru/staff/students/?${payload.getSubmitUrlPath(sesid ?: "")}&PAGEN_1=$page")
|
.connect("https://lk.mirea.ru/staff/students/?${payload.getSubmitUrlPath(sesid ?: "")}&PAGEN_1=$page")
|
||||||
.cookies(cookies)
|
.cookies(cookies)
|
||||||
.referrer("https://lk.mirea.ru/")
|
.referrer("https://lk.mirea.ru/")
|
||||||
.execute().parse()
|
.execute()
|
||||||
|
|
||||||
|
val doc = conn.parse()
|
||||||
|
|
||||||
|
return doc
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Document.getTotalPages() = kotlin.runCatching {
|
fun Document.getTotalPages() = kotlin.runCatching {
|
||||||
this.select("body > div.container > div.bx-pagination > div > ul > li:nth-child(6) > a > span").text().toInt()
|
this.select("body > div.container > div.bx-pagination > div > ul > li:nth-child(6) > a > span").text().toInt()
|
||||||
}.getOrNull() ?: 0
|
}.getOrNull() ?: 0
|
||||||
|
|
||||||
fun Document.findStudentsInPage(): LksResponse {
|
fun findStudentsInPage(document: Document): LksResponse {
|
||||||
|
|
||||||
val table = this.select("body > div.container > table > tbody") ?: return LksResponse()
|
val table = document.select("body > div.container > table > tbody") ?: return LksResponse()
|
||||||
|
|
||||||
val students = table.select("tr").map {
|
val students = table.select("tr").map {
|
||||||
|
|
||||||
@@ -88,7 +95,7 @@ fun Document.findStudentsInPage(): LksResponse {
|
|||||||
|
|
||||||
return LksResponse(
|
return LksResponse(
|
||||||
students = students,
|
students = students,
|
||||||
availablePages = this.getTotalPages()
|
availablePages = document.getTotalPages()
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -31,8 +31,8 @@ class DataModule {
|
|||||||
fun providesDefaultCookies() = mapOf(
|
fun providesDefaultCookies() = mapOf(
|
||||||
"PHPSESSID" to "deleted",
|
"PHPSESSID" to "deleted",
|
||||||
"BITRIX_SM_SOUND_LOGIN_PLAYED" to "Y",
|
"BITRIX_SM_SOUND_LOGIN_PLAYED" to "Y",
|
||||||
"BITRIX_SM_UIDH" to "00000000000000000000000000000000",
|
"BITRIX_SM_UIDH" to "22f459c5c83cff9cd62c99693b00c361",
|
||||||
"BITRIX_SM_UIDL" to "ivachev.a.a%40edu.mirea.ru"
|
"BITRIX_SM_UIDL" to "kits.m.a%40edu.mirea.ru"
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,22 @@
|
|||||||
package ru.megboyzz.lks.profile.data.repositories
|
package ru.megboyzz.lks.profile.data.repositories
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.util.Log
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import ru.megboyzz.lks.profile.data.findStudentsInPage
|
import ru.megboyzz.lks.profile.data.findStudentsInPage
|
||||||
|
import ru.megboyzz.lks.profile.data.getProfilePage
|
||||||
import ru.megboyzz.lks.profile.data.signInLks
|
import ru.megboyzz.lks.profile.data.signInLks
|
||||||
import ru.megboyzz.lks.profile.domain.CookiesPrefs
|
import ru.megboyzz.lks.profile.domain.CookiesPrefs
|
||||||
import ru.megboyzz.lks.profile.domain.entities.LksRequest
|
import ru.megboyzz.lks.profile.domain.entities.LksRequest
|
||||||
import ru.megboyzz.lks.profile.domain.entities.LksResponse
|
import ru.megboyzz.lks.profile.domain.entities.LksResponse
|
||||||
|
import ru.megboyzz.lks.profile.domain.entities.LksStudent
|
||||||
import ru.megboyzz.lks.profile.domain.repositories.LksRepository
|
import ru.megboyzz.lks.profile.domain.repositories.LksRepository
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class LksRepositoryImpl @Inject constructor(
|
class LksRepositoryImpl @Inject constructor(
|
||||||
@CookiesPrefs
|
@CookiesPrefs
|
||||||
private val sharedPreferences: SharedPreferences
|
private val sharedPreferences: SharedPreferences,
|
||||||
|
|
||||||
) : LksRepository {
|
) : LksRepository {
|
||||||
|
|
||||||
private val cookieNamesKey = "cookie_names"
|
private val cookieNamesKey = "cookie_names"
|
||||||
@@ -23,7 +27,16 @@ class LksRepositoryImpl @Inject constructor(
|
|||||||
): Map<String, String> = signInLks(username, password)
|
): Map<String, String> = signInLks(username, password)
|
||||||
|
|
||||||
override suspend fun cookiesIsValid(cookie: Map<String, String>): Boolean {
|
override suspend fun cookiesIsValid(cookie: Map<String, String>): Boolean {
|
||||||
return true
|
|
||||||
|
if(cookie.isEmpty()) return false
|
||||||
|
|
||||||
|
val document = getProfilePage(LksRequest(), cookie, 1)
|
||||||
|
|
||||||
|
val pages =
|
||||||
|
document.select("body > div.container > div.bx-pagination > div > ul > li:nth-child(6) > a > span")
|
||||||
|
.text()
|
||||||
|
|
||||||
|
return pages.isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -47,9 +60,13 @@ class LksRepositoryImpl @Inject constructor(
|
|||||||
|
|
||||||
override suspend fun getPage(lksRequest: LksRequest, cookie: Map<String, String>, pageNumber: Int): Result<LksResponse> {
|
override suspend fun getPage(lksRequest: LksRequest, cookie: Map<String, String>, pageNumber: Int): Result<LksResponse> {
|
||||||
|
|
||||||
val document = ru.megboyzz.lks.profile.data.getPage(lksRequest, cookie, pageNumber)
|
Log.i("TEXT", "Its begin doc!")
|
||||||
|
val document = getProfilePage(lksRequest, cookie, pageNumber)
|
||||||
|
Log.i("TEXT", "Its begin end!")
|
||||||
|
|
||||||
val page = document.findStudentsInPage()
|
Log.i("TEXT", "Its begin!")
|
||||||
|
val page = findStudentsInPage(document)
|
||||||
|
Log.i("TEXT", "Its end!")
|
||||||
|
|
||||||
return if(page == LksResponse())
|
return if(page == LksResponse())
|
||||||
Result.failure(Exception())
|
Result.failure(Exception())
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
package ru.megboyzz.lks.profile.data.repositories
|
package ru.megboyzz.lks.profile.data.repositories
|
||||||
|
|
||||||
import ru.megboyzz.lks.profile.domain.entities.LksResponse
|
import ru.megboyzz.lks.profile.domain.entities.LksStudent
|
||||||
import ru.megboyzz.lks.profile.domain.repositories.StudentRepository
|
import ru.megboyzz.lks.profile.domain.repositories.StudentRepository
|
||||||
|
|
||||||
class StudentRepositoryImpl : StudentRepository {
|
class StudentRepositoryImpl : StudentRepository {
|
||||||
override fun getAllFavStudents(): List<LksResponse> {
|
override fun getAllFavStudents(): List<LksStudent> {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addStudent(lksResponse: LksResponse) {
|
override fun likeStudent(lksResponse: LksStudent) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deleteFormFav(lksResponse: LksResponse) {
|
override fun deleteFormFav(lksResponse: LksStudent) {
|
||||||
TODO("Not yet implemented")
|
TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,5 +12,5 @@ data class LksRequest(
|
|||||||
* Метод для получения пути после https://lk.mirea.ru/staff/students? ...
|
* Метод для получения пути после https://lk.mirea.ru/staff/students? ...
|
||||||
*/
|
*/
|
||||||
fun getSubmitUrlPath(sesid: String) =
|
fun getSubmitUrlPath(sesid: String) =
|
||||||
"sessid=$sesid&findname=${student}&findgroup=${group ?: ""}&finddepartment=${department ?: ""}&submit-filter=Найти"
|
"sessid=$sesid&findname=${student ?: ""}&findgroup=${group ?: ""}&finddepartment=${department ?: ""}&submit-filter=Найти"
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,10 @@ package ru.megboyzz.lks.profile.domain.repositories
|
|||||||
|
|
||||||
import ru.megboyzz.lks.profile.domain.entities.LksRequest
|
import ru.megboyzz.lks.profile.domain.entities.LksRequest
|
||||||
import ru.megboyzz.lks.profile.domain.entities.LksResponse
|
import ru.megboyzz.lks.profile.domain.entities.LksResponse
|
||||||
|
import ru.megboyzz.lks.profile.domain.entities.LksStudent
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
interface LksRepository {
|
interface LksRepository: Serializable {
|
||||||
|
|
||||||
suspend fun signInAndGetCookies(username: String, password: String): Map<String, String>
|
suspend fun signInAndGetCookies(username: String, password: String): Map<String, String>
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package ru.megboyzz.lks.profile.domain.repositories
|
package ru.megboyzz.lks.profile.domain.repositories
|
||||||
|
|
||||||
import ru.megboyzz.lks.profile.domain.entities.LksResponse
|
import ru.megboyzz.lks.profile.domain.entities.LksStudent
|
||||||
|
|
||||||
interface StudentRepository {
|
interface StudentRepository {
|
||||||
|
|
||||||
fun getAllFavStudents(): List<LksResponse>
|
fun getAllFavStudents(): List<LksStudent>
|
||||||
|
|
||||||
fun addStudent(lksResponse: LksResponse)
|
fun likeStudent(lksResponse: LksStudent)
|
||||||
|
|
||||||
fun deleteFormFav(lksResponse: LksResponse)
|
fun deleteFormFav(lksResponse: LksStudent)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
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,72 @@
|
|||||||
|
package ru.megboyzz.lks.profile.ui
|
||||||
|
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.combinedClickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.material.ripple.rememberRipple
|
||||||
|
import androidx.compose.material3.ripple
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
|
@Composable
|
||||||
|
fun Modifier.clickableWithNormalRipple(
|
||||||
|
onClick: () -> Unit = {},
|
||||||
|
onLongClick: () -> Unit = {},
|
||||||
|
) = this.combinedClickable(
|
||||||
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
|
indication = ripple(bounded = true),
|
||||||
|
onClick = onClick,
|
||||||
|
onLongClick = onLongClick
|
||||||
|
)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
|
@Composable
|
||||||
|
fun Modifier.clickableWithNoEffect(
|
||||||
|
onClick: () -> Unit = {},
|
||||||
|
onLongClick: () -> Unit = {},
|
||||||
|
) = this.combinedClickable(
|
||||||
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
|
indication = null,
|
||||||
|
onClick = onClick,
|
||||||
|
onLongClick = onLongClick
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Int.asPainter() = painterResource(this)
|
||||||
|
|
||||||
|
fun Context.copyToClipboard(content: String) {
|
||||||
|
val clipboardManager = this.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
|
|
||||||
|
val clip = ClipData.newPlainText("label", content)
|
||||||
|
|
||||||
|
clipboardManager.setPrimaryClip(clip)
|
||||||
|
|
||||||
|
Toast.makeText(this, "Текст скопирован в буфер обмена", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.goToVK(request: String) {
|
||||||
|
val browserIntent = Intent(
|
||||||
|
Intent.ACTION_VIEW,
|
||||||
|
Uri.parse("https://vk.com/search?q=$request")
|
||||||
|
)
|
||||||
|
this.startActivity(browserIntent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.getDepartmentDict() : Map<String, String> {
|
||||||
|
val jsonString = assets.open("dict.json").bufferedReader().use { it.readText() }
|
||||||
|
return Json.decodeFromString(jsonString)
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package ru.megboyzz.lks.profile.ui.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.DropdownMenu
|
||||||
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||||
|
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextField
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SimpleDropdownMenu(
|
||||||
|
expanded: Boolean,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
content: @Composable ColumnScope.() -> Unit
|
||||||
|
) = DropdownMenu(
|
||||||
|
expanded = expanded,
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
content = content
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SimpleDropdownMenuItem(
|
||||||
|
text: String,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) = DropdownMenuItem(
|
||||||
|
text = { Text(text) },
|
||||||
|
onClick = onClick
|
||||||
|
)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun DropdownSelectableSearch(
|
||||||
|
) {
|
||||||
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
val options = listOf("Option 1", "Option 2", "Option 3", "Option 4", "Option 5")
|
||||||
|
var selectedOptionText by remember { mutableStateOf(options[0]) }
|
||||||
|
|
||||||
|
ExposedDropdownMenuBox(
|
||||||
|
expanded = expanded,
|
||||||
|
onExpandedChange = { expanded != expanded }
|
||||||
|
) {
|
||||||
|
TextField(
|
||||||
|
readOnly = true,
|
||||||
|
modifier = Modifier.clickable {
|
||||||
|
expanded != expanded
|
||||||
|
},
|
||||||
|
value = selectedOptionText,
|
||||||
|
onValueChange = { },
|
||||||
|
label = { Text("Label") },
|
||||||
|
trailingIcon = {
|
||||||
|
ExposedDropdownMenuDefaults.TrailingIcon(
|
||||||
|
expanded = expanded
|
||||||
|
)
|
||||||
|
},
|
||||||
|
colors = ExposedDropdownMenuDefaults.textFieldColors(),
|
||||||
|
)
|
||||||
|
ExposedDropdownMenu(
|
||||||
|
expanded = expanded,
|
||||||
|
onDismissRequest = {}
|
||||||
|
) {
|
||||||
|
options.forEach { selectionOption ->
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = { Text(text = selectionOption) },
|
||||||
|
onClick = {
|
||||||
|
selectedOptionText = selectionOption
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun DropdownSearchPrev() {
|
||||||
|
Box(Modifier.padding(top =100.dp)) {
|
||||||
|
DropdownSelectableSearch()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package ru.megboyzz.lks.profile.ui.components
|
||||||
|
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.navigation.compose.NavHost
|
||||||
|
import androidx.navigation.compose.composable
|
||||||
|
import androidx.navigation.compose.rememberNavController
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.Serializer
|
||||||
|
import ru.megboyzz.lks.profile.ui.model.SignInViewModel
|
||||||
|
import ru.megboyzz.lks.profile.ui.provider.ActivityProvider
|
||||||
|
import ru.megboyzz.lks.profile.ui.provider.LocalMainActivity
|
||||||
|
import ru.megboyzz.lks.profile.ui.screens.SearchScreenContent
|
||||||
|
import ru.megboyzz.lks.profile.ui.screens.SignInScreenContent
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Место где лучше всего применять все CompositionProvider
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MainNavHost() = ActivityProvider {
|
||||||
|
|
||||||
|
val navHostController = rememberNavController()
|
||||||
|
|
||||||
|
val viewModel by LocalMainActivity.current.viewModels<SignInViewModel>()
|
||||||
|
|
||||||
|
val state by viewModel.state.collectAsState()
|
||||||
|
|
||||||
|
if(state is SignInViewModel.SignInState.Success) {
|
||||||
|
navHostController.navigate(SearchScreen)
|
||||||
|
}
|
||||||
|
|
||||||
|
NavHost(
|
||||||
|
navController = navHostController,
|
||||||
|
startDestination = SignInScreen
|
||||||
|
) {
|
||||||
|
composable<SignInScreen> {
|
||||||
|
SignInScreenContent()
|
||||||
|
}
|
||||||
|
composable<SearchScreen> {
|
||||||
|
SearchScreenContent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
object SearchScreen
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
object SignInScreen
|
||||||
@@ -0,0 +1,358 @@
|
|||||||
|
package ru.megboyzz.lks.profile.ui.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
|
||||||
|
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
||||||
|
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
||||||
|
import androidx.compose.material.icons.outlined.AccountCircle
|
||||||
|
import androidx.compose.material.icons.outlined.Person
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.DropdownMenu
|
||||||
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.OutlinedTextFieldDefaults
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.painter.Painter
|
||||||
|
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||||
|
import androidx.compose.ui.layout.onSizeChanged
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import ru.megboyzz.lks.profile.R
|
||||||
|
import ru.megboyzz.lks.profile.domain.entities.LksRequest
|
||||||
|
import ru.megboyzz.lks.profile.domain.entities.LksResponse
|
||||||
|
import ru.megboyzz.lks.profile.domain.entities.LksStudent
|
||||||
|
import ru.megboyzz.lks.profile.domain.entities.Student
|
||||||
|
import ru.megboyzz.lks.profile.ui.asPainter
|
||||||
|
import ru.megboyzz.lks.profile.ui.clickableWithNormalRipple
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ContextMenuContainer(
|
||||||
|
content: @Composable ColumnScope.() -> Unit
|
||||||
|
) {
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 4.dp, vertical = 8.dp)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.navigationBarsPadding(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(32.dp, 4.dp)
|
||||||
|
.clip(RoundedCornerShape(80))
|
||||||
|
.background(MaterialTheme.colorScheme.onBackground)
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(16.dp))
|
||||||
|
Column (
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 12.dp)
|
||||||
|
.padding(bottom = 12.dp),
|
||||||
|
content = content
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun ContextMenuContainerPrev() {
|
||||||
|
|
||||||
|
ContextMenuContainer {
|
||||||
|
|
||||||
|
Text("Hello!")
|
||||||
|
Text("Hello!")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SearchTextField(
|
||||||
|
value: String,
|
||||||
|
onValueChange: (String) -> Unit,
|
||||||
|
suggestions: List<String>? = null,
|
||||||
|
label: String? = null
|
||||||
|
) {
|
||||||
|
|
||||||
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
var dropDownWidth by remember { mutableIntStateOf(0) }
|
||||||
|
|
||||||
|
val icon = if (expanded)
|
||||||
|
Icons.Filled.KeyboardArrowUp
|
||||||
|
else
|
||||||
|
Icons.Filled.KeyboardArrowDown
|
||||||
|
|
||||||
|
|
||||||
|
Column {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = value,
|
||||||
|
onValueChange = onValueChange,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.onSizeChanged { dropDownWidth = it.width },
|
||||||
|
label = { Text(label ?: "Label") },
|
||||||
|
trailingIcon = {
|
||||||
|
if(suggestions != null)
|
||||||
|
Icon(
|
||||||
|
icon,
|
||||||
|
"contentDescription",
|
||||||
|
Modifier.clickableWithNormalRipple{ expanded = !expanded }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = expanded,
|
||||||
|
onDismissRequest = { expanded = false },
|
||||||
|
modifier = Modifier
|
||||||
|
.width(with(LocalDensity.current){dropDownWidth.toDp()})
|
||||||
|
) {
|
||||||
|
suggestions?.forEach { label ->
|
||||||
|
DropdownMenuItem(
|
||||||
|
onClick = { onValueChange(label) },
|
||||||
|
text = { Text(text = label) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun SearchTextFieldPrev() {
|
||||||
|
|
||||||
|
val suggestions = listOf("Item1","Item2","Item3")
|
||||||
|
var value by remember { mutableStateOf("") }
|
||||||
|
|
||||||
|
SearchTextField(
|
||||||
|
value = value,
|
||||||
|
onValueChange = { value = it },
|
||||||
|
suggestions = suggestions
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SearchContextMenu(
|
||||||
|
initialState: LksRequest,
|
||||||
|
onSearch: (LksRequest) -> Unit
|
||||||
|
) {
|
||||||
|
var name by remember { mutableStateOf(initialState.student?.name ?: "") }
|
||||||
|
var surname by remember { mutableStateOf(initialState.student?.patronymic ?: "") }
|
||||||
|
var family by remember { mutableStateOf(initialState.student?.surname ?: "") }
|
||||||
|
|
||||||
|
var group by remember { mutableStateOf(initialState.group ?: "") }
|
||||||
|
var departamnet by remember { mutableStateOf("") }
|
||||||
|
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
modifier = Modifier.padding(horizontal = 24.dp)
|
||||||
|
) {
|
||||||
|
|
||||||
|
SearchTextField(
|
||||||
|
value = family,
|
||||||
|
onValueChange = {family = it},
|
||||||
|
label = "Фамилия"
|
||||||
|
)
|
||||||
|
|
||||||
|
SearchTextField(
|
||||||
|
value = name,
|
||||||
|
onValueChange = {name = it},
|
||||||
|
label = "Имя"
|
||||||
|
)
|
||||||
|
|
||||||
|
SearchTextField(
|
||||||
|
value = surname,
|
||||||
|
onValueChange = {surname = it},
|
||||||
|
label = "Отчество"
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(Modifier.height(1.dp))
|
||||||
|
HorizontalDivider(color = MaterialTheme.colorScheme.onBackground)
|
||||||
|
|
||||||
|
SearchTextField(
|
||||||
|
value = group,
|
||||||
|
onValueChange = {group = it},
|
||||||
|
label = "Учебная группа"
|
||||||
|
)
|
||||||
|
|
||||||
|
SearchTextField(
|
||||||
|
value = departamnet,
|
||||||
|
onValueChange = { departamnet = it },
|
||||||
|
label = "Подразделение"
|
||||||
|
)
|
||||||
|
|
||||||
|
Button(
|
||||||
|
shape = OutlinedTextFieldDefaults.shape,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
onClick = {
|
||||||
|
val lksRequest = LksRequest(
|
||||||
|
student = Student(
|
||||||
|
name = name.ifEmpty { null },
|
||||||
|
surname = family.ifEmpty { null },
|
||||||
|
patronymic = surname.ifEmpty { null },
|
||||||
|
),
|
||||||
|
group = group.ifEmpty { null },
|
||||||
|
department = departamnet.ifEmpty { null }
|
||||||
|
)
|
||||||
|
onSearch(lksRequest)
|
||||||
|
//bottomSheetNavigator?.hide()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text("Поиск")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun SearchContextMenuPrev() {
|
||||||
|
|
||||||
|
SearchContextMenu(
|
||||||
|
initialState = LksRequest()
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ContextMenuButton(
|
||||||
|
icon: Painter,
|
||||||
|
label: String,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(0.dp)
|
||||||
|
.clickableWithNormalRipple(onClick = onClick)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = icon,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = Color.Unspecified,
|
||||||
|
modifier = Modifier.size(36.dp)
|
||||||
|
)
|
||||||
|
Text(text = label)
|
||||||
|
}
|
||||||
|
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun ContextMenuButtonPrev() {
|
||||||
|
|
||||||
|
ContextMenuButton(
|
||||||
|
icon = R.drawable.vk_ic.asPainter(),
|
||||||
|
label = "Переидти в VK",
|
||||||
|
onClick = {}
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LongPressCardContextMenu(
|
||||||
|
lksStudent: LksStudent,
|
||||||
|
onGoToVk: () -> Unit,
|
||||||
|
onCopyName: (String) -> Unit,
|
||||||
|
onCopyGroup: (String) -> Unit
|
||||||
|
) {
|
||||||
|
Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||||
|
|
||||||
|
Text(
|
||||||
|
"${lksStudent.studentFullName} ${lksStudent.group}"
|
||||||
|
)
|
||||||
|
HorizontalDivider(color = MaterialTheme.colorScheme.onBackground)
|
||||||
|
ContextMenuButton(
|
||||||
|
icon = R.drawable.vk_ic.asPainter(),
|
||||||
|
label = "Переидти в VK",
|
||||||
|
onClick = onGoToVk
|
||||||
|
)
|
||||||
|
ContextMenuButton(
|
||||||
|
icon = rememberVectorPainter(Icons.Outlined.AccountCircle),
|
||||||
|
label = "Скопировать имя пользователя",
|
||||||
|
onClick = { onCopyName.invoke(lksStudent.studentFullName ?: "") }
|
||||||
|
)
|
||||||
|
ContextMenuButton(
|
||||||
|
icon = rememberVectorPainter(Icons.Outlined.Person),
|
||||||
|
label = "Скопировать группу",
|
||||||
|
onClick = { onCopyGroup.invoke(lksStudent.group ?: "") }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun LongPressCardContextMenuPrev() {
|
||||||
|
|
||||||
|
val lksStudent = LksStudent(
|
||||||
|
studentFullName = "Киц Михаил Андреевич",
|
||||||
|
group = "ИКБО-03-20"
|
||||||
|
)
|
||||||
|
|
||||||
|
LongPressCardContextMenu(
|
||||||
|
lksStudent = lksStudent,
|
||||||
|
onGoToVk = {},
|
||||||
|
onCopyName = {},
|
||||||
|
onCopyGroup = {}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,14 +1,254 @@
|
|||||||
package ru.megboyzz.lks.profile.ui.components
|
package ru.megboyzz.lks.profile.ui.components
|
||||||
|
|
||||||
import android.provider.ContactsContract.Profile
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.foundation.BorderStroke
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.defaultMinSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.MoreVert
|
||||||
|
import androidx.compose.material.icons.filled.Search
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.FloatingActionButton
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import ru.megboyzz.lks.profile.domain.entities.LksResponse
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import ru.megboyzz.lks.profile.domain.entities.LksStudent
|
import ru.megboyzz.lks.profile.domain.entities.LksStudent
|
||||||
|
import ru.megboyzz.lks.profile.ui.clickableWithNoEffect
|
||||||
|
import ru.megboyzz.lks.profile.ui.clickableWithNormalRipple
|
||||||
|
import ru.megboyzz.lks.profile.ui.theme.MainTheme
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ProfileCard(lksStudent: LksStudent) {
|
fun ProfileCard(
|
||||||
|
lksStudent: LksStudent,
|
||||||
|
onLongPress: () -> Unit
|
||||||
|
) {
|
||||||
|
|
||||||
|
var toggled by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.border(
|
||||||
|
border = BorderStroke(1.dp, MaterialTheme.colorScheme.primary),
|
||||||
|
shape = RoundedCornerShape(16.dp)
|
||||||
|
)
|
||||||
|
.defaultMinSize(minWidth = 240.dp)
|
||||||
|
.clip(RoundedCornerShape(16.dp))
|
||||||
|
.clickableWithNormalRipple(
|
||||||
|
onClick = { toggled = !toggled },
|
||||||
|
onLongClick = onLongPress,
|
||||||
|
),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(12.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = lksStudent.studentFullName ?: "<Нет имени>"
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = lksStudent.group ?: "<Нет группы>"
|
||||||
|
)
|
||||||
|
AnimatedVisibility(toggled) {
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = 4.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
HorizontalDivider(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = lksStudent.department ?: "<Нет подразделения>"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun MainScaffold(
|
||||||
|
onFloatingButtonClick: () -> Unit,
|
||||||
|
onTopClick: () -> Unit = {},
|
||||||
|
content: @Composable (PaddingValues) -> Unit
|
||||||
|
) {
|
||||||
|
|
||||||
|
var expanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
Surface(
|
||||||
|
shadowElevation = 8.dp,
|
||||||
|
modifier = Modifier.clickableWithNoEffect(onTopClick)
|
||||||
|
) {
|
||||||
|
TopAppBar(
|
||||||
|
title = { Text("LKS Search") },
|
||||||
|
actions = {
|
||||||
|
IconButton(onClick = { expanded =! expanded }) {
|
||||||
|
Icon(Icons.Default.MoreVert, contentDescription = "More options")
|
||||||
|
}
|
||||||
|
SimpleDropdownMenu(
|
||||||
|
expanded = expanded,
|
||||||
|
onDismiss = { expanded =! expanded },
|
||||||
|
) {
|
||||||
|
SimpleDropdownMenuItem("История поиска") {
|
||||||
|
expanded =! expanded
|
||||||
|
}
|
||||||
|
SimpleDropdownMenuItem("Избранное") {
|
||||||
|
expanded =! expanded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
floatingActionButton = {
|
||||||
|
FloatingActionButton(onFloatingButtonClick) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Search,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
content = content
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun ProfileCardPrev() {
|
||||||
|
|
||||||
|
val student = LksStudent(
|
||||||
|
studentFullName = "Киц Михаил Андреевич",
|
||||||
|
department = "ИИТ",
|
||||||
|
group = "ИКБО-03-20"
|
||||||
|
)
|
||||||
|
|
||||||
|
ProfileCard(
|
||||||
|
lksStudent = student
|
||||||
|
) { }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun Main() {
|
||||||
|
|
||||||
|
val student = LksStudent(
|
||||||
|
studentFullName = "Киц Михаил Андреевич",
|
||||||
|
department = "ИИТ",
|
||||||
|
group = "ИКБО-03-20"
|
||||||
|
)
|
||||||
|
|
||||||
|
MainTheme {
|
||||||
|
MainScaffold(
|
||||||
|
onFloatingButtonClick = {}
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.padding(it)) {
|
||||||
|
ProfileCard(
|
||||||
|
lksStudent = student
|
||||||
|
) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CardsColumn(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
lksStudents: List<LksStudent>,
|
||||||
|
onLongPress: (LksStudent) -> Unit,
|
||||||
|
onColumnOverflow: suspend () -> Unit,
|
||||||
|
) {
|
||||||
|
|
||||||
|
val buffer = 1
|
||||||
|
|
||||||
|
var isLoading by remember { mutableStateOf(false) }
|
||||||
|
val listState = rememberLazyListState()
|
||||||
|
|
||||||
|
// observe list scrolling
|
||||||
|
val reachedBottom: Boolean by remember {
|
||||||
|
derivedStateOf {
|
||||||
|
val lastVisibleItem = listState.layoutInfo.visibleItemsInfo.lastOrNull()
|
||||||
|
lastVisibleItem?.index != 0 && lastVisibleItem?.index == listState.layoutInfo.totalItemsCount - buffer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// load more if scrolled to bottom
|
||||||
|
LaunchedEffect(reachedBottom) {
|
||||||
|
if (reachedBottom) {
|
||||||
|
isLoading = true
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
onColumnOverflow()
|
||||||
|
}
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
state = listState,
|
||||||
|
contentPadding = PaddingValues(12.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||||
|
) {
|
||||||
|
items(lksStudents) { student ->
|
||||||
|
ProfileCard(
|
||||||
|
lksStudent = student,
|
||||||
|
onLongPress = { onLongPress(student) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if(isLoading)
|
||||||
|
item {
|
||||||
|
CircularProgressIndicator()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package ru.megboyzz.lks.profile.ui.components
|
||||||
|
|
||||||
|
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.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Info
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LoadingShield(title: String? = null) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
CircularProgressIndicator()
|
||||||
|
if(title != null)
|
||||||
|
Text(title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ErrorShield(title: String? = null) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Info,
|
||||||
|
contentDescription = null
|
||||||
|
)
|
||||||
|
if(title != null)
|
||||||
|
Text(title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
package ru.megboyzz.lks.profile.ui.model
|
|
||||||
|
|
||||||
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 @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()
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package ru.megboyzz.lks.profile.ui.model
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
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
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class SearchViewModel @Inject constructor(
|
||||||
|
private val lksRepository: LksRepository,
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
sealed class UiState(val status: String? = null) {
|
||||||
|
data object Loading: UiState()
|
||||||
|
//data object Success: SignInState("Успешный вход")
|
||||||
|
data class Error(val _status: String) : UiState(_status)
|
||||||
|
data object Idle: UiState()
|
||||||
|
}
|
||||||
|
|
||||||
|
val state = MutableStateFlow<UiState>(UiState.Idle)
|
||||||
|
|
||||||
|
suspend fun nextPage() {
|
||||||
|
if(_responseList.value.availablePages > _curPageNumber.value) {
|
||||||
|
_curPageNumber.emit(_curPageNumber.value + 1)
|
||||||
|
update(_request.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun resetStudents() {
|
||||||
|
_curPageNumber.value = 1
|
||||||
|
_responseList.emit(LksResponse())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun newSearch(request: LksRequest) {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
state.emit(UiState.Loading)
|
||||||
|
resetStudents()
|
||||||
|
state.emit(update(request))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun update(request: LksRequest): UiState {
|
||||||
|
|
||||||
|
_request.emit(request)
|
||||||
|
|
||||||
|
val repoCookies = lksRepository.restoreCooke()
|
||||||
|
|
||||||
|
val result = lksRepository.getPage(request, repoCookies, curPageNumber.value)
|
||||||
|
|
||||||
|
if(result.isFailure)
|
||||||
|
return UiState.Error("Ошибка :-(")
|
||||||
|
|
||||||
|
val list = result.getOrElse { LksResponse() }
|
||||||
|
|
||||||
|
val updatedStudents = LksResponse(
|
||||||
|
students = _responseList.value.students + list.students,
|
||||||
|
availablePages = list.availablePages
|
||||||
|
)
|
||||||
|
|
||||||
|
_responseList.emit(updatedStudents)
|
||||||
|
|
||||||
|
return UiState.Idle
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,26 +1,28 @@
|
|||||||
package ru.megboyzz.lks.profile.ui.model
|
package ru.megboyzz.lks.profile.ui.model
|
||||||
|
|
||||||
import cafe.adriel.voyager.core.model.ScreenModel
|
import android.content.Context
|
||||||
import cafe.adriel.voyager.core.model.screenModelScope
|
import android.widget.Toast
|
||||||
import cafe.adriel.voyager.hilt.ScreenModelFactory
|
import androidx.lifecycle.ViewModel
|
||||||
import cafe.adriel.voyager.navigator.Navigator
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.assisted.Assisted
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
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.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 ru.megboyzz.lks.profile.domain.DefaultCookies
|
||||||
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 javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
class SignInScreenModel @AssistedInject constructor(
|
class SignInViewModel @Inject constructor(
|
||||||
|
@ApplicationContext val context: Context,
|
||||||
private val lksRepository: LksRepository,
|
private val lksRepository: LksRepository,
|
||||||
@Assisted private val navigator: Navigator
|
@DefaultCookies
|
||||||
) : ScreenModel {
|
private val defaultCookies: Map<String, String>
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _username = MutableStateFlow("")
|
private val _username = MutableStateFlow("")
|
||||||
val username = _username.asStateFlow()
|
val username = _username.asStateFlow()
|
||||||
@@ -37,19 +39,40 @@ class SignInScreenModel @AssistedInject constructor(
|
|||||||
|
|
||||||
val state = MutableStateFlow<SignInState>(SignInState.Idle)
|
val state = MutableStateFlow<SignInState>(SignInState.Idle)
|
||||||
|
|
||||||
|
private val _loadingScreen = MutableStateFlow(false)
|
||||||
|
val loadingScreen = _loadingScreen.asStateFlow()
|
||||||
|
|
||||||
private var cookie: Map<String, String> = mapOf()
|
private var cookie: Map<String, String> = mapOf()
|
||||||
|
|
||||||
init {
|
private fun makeSuspendToast(text: String) = viewModelScope.launch(Dispatchers.Main) {
|
||||||
|
Toast.makeText(context, text, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
|
||||||
screenModelScope.launch {
|
private var isUseDefaultCookie = false
|
||||||
|
|
||||||
|
init {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
state.emit(SignInState.Loading)
|
||||||
cookie = lksRepository.restoreCooke()
|
cookie = lksRepository.restoreCooke()
|
||||||
if(lksRepository.cookiesIsValid(cookie))
|
if(cookie.isEmpty()) {
|
||||||
navigator.push(SearchScreen())
|
isUseDefaultCookie = true
|
||||||
|
cookie = defaultCookies
|
||||||
|
}
|
||||||
|
if(lksRepository.cookiesIsValid(cookie)) {
|
||||||
|
state.emit(SignInState.Success)
|
||||||
|
if(isUseDefaultCookie)
|
||||||
|
makeSuspendToast("Успех с дефолтными куками!")
|
||||||
|
else
|
||||||
|
makeSuspendToast("Успех!")
|
||||||
|
}else {
|
||||||
|
state.emit(SignInState.Error("Error!"))
|
||||||
|
makeSuspendToast("Не успех!")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.onEach {
|
state.onEach {
|
||||||
if(it == SignInState.Success) navigator.push(SearchScreen())
|
//if(it == SignInState.Success) navigator.push(SearchScreen())
|
||||||
}.launchIn(screenModelScope)
|
}.launchIn(viewModelScope)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +85,7 @@ class SignInScreenModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun signIn() {
|
fun signIn() {
|
||||||
screenModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
state.emit(SignInState.Loading)
|
state.emit(SignInState.Loading)
|
||||||
cookie = lksRepository.signInAndGetCookies(_username.value, _password.value)
|
cookie = lksRepository.signInAndGetCookies(_username.value, _password.value)
|
||||||
if(cookie.isEmpty())
|
if(cookie.isEmpty())
|
||||||
@@ -73,10 +96,4 @@ class SignInScreenModel @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@AssistedFactory
|
|
||||||
interface Factory : ScreenModelFactory {
|
|
||||||
fun create(navigator: Navigator): SignInScreenModel
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package ru.megboyzz.lks.profile.ui.provider
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import ru.megboyzz.lks.profile.MainActivity
|
||||||
|
|
||||||
|
val LocalMainActivity = compositionLocalOf<MainActivity> {
|
||||||
|
error("No MainActivity provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ActivityProvider(
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
val mainActivity = LocalContext.current as MainActivity
|
||||||
|
CompositionLocalProvider(LocalMainActivity provides mainActivity) {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,45 +1,63 @@
|
|||||||
package ru.megboyzz.lks.profile.ui.screens
|
package ru.megboyzz.lks.profile.ui.screens
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.activity.viewModels
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
import androidx.compose.material3.OutlinedButton
|
|
||||||
import androidx.compose.material3.OutlinedTextField
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.pointer.PointerIcon.Companion.Text
|
import ru.megboyzz.lks.profile.domain.entities.LksStudent
|
||||||
import androidx.compose.ui.unit.dp
|
import ru.megboyzz.lks.profile.ui.components.CardsColumn
|
||||||
import cafe.adriel.voyager.core.screen.Screen
|
import ru.megboyzz.lks.profile.ui.components.ErrorShield
|
||||||
import cafe.adriel.voyager.hilt.getScreenModel
|
import ru.megboyzz.lks.profile.ui.components.LoadingShield
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
import ru.megboyzz.lks.profile.ui.components.MainScaffold
|
||||||
import ru.megboyzz.lks.profile.ui.model.SearchScreenModel
|
import ru.megboyzz.lks.profile.ui.model.SearchViewModel
|
||||||
|
import ru.megboyzz.lks.profile.ui.provider.LocalMainActivity
|
||||||
|
import ru.megboyzz.lks.profile.ui.screens.sub.LongPressMenu
|
||||||
|
import ru.megboyzz.lks.profile.ui.screens.sub.SearchBottomSheet
|
||||||
|
|
||||||
class SearchScreen : Screen {
|
@Composable
|
||||||
@Composable
|
fun SearchScreenContent() {
|
||||||
override fun Content() {
|
|
||||||
|
|
||||||
val screenModel = getScreenModel<SearchScreenModel>()
|
val screenModel by LocalMainActivity.current.viewModels<SearchViewModel>()
|
||||||
|
|
||||||
Box(
|
val state by screenModel.state.collectAsState()
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
contentAlignment = Alignment.TopCenter
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
|
||||||
) {
|
|
||||||
Spacer(Modifier.height(20.dp))
|
|
||||||
val navigator = LocalNavigator.current!!
|
|
||||||
|
|
||||||
|
val list by screenModel.responseList.collectAsState()
|
||||||
|
|
||||||
|
var searchMenuExpanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
var isLongPressedStudent by remember {
|
||||||
|
mutableStateOf<LksStudent?>(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
MainScaffold(
|
||||||
|
onFloatingButtonClick = { searchMenuExpanded =! searchMenuExpanded },
|
||||||
|
onTopClick = { }
|
||||||
|
) {
|
||||||
|
when (state) {
|
||||||
|
is SearchViewModel.UiState.Loading -> LoadingShield("Загрузка челиков")
|
||||||
|
is SearchViewModel.UiState.Error -> ErrorShield(state.status)
|
||||||
|
else -> {
|
||||||
|
CardsColumn(
|
||||||
|
modifier = Modifier.padding(it),
|
||||||
|
lksStudents = list.students,
|
||||||
|
onLongPress = { student -> isLongPressedStudent = student },
|
||||||
|
onColumnOverflow = {
|
||||||
|
screenModel.nextPage()
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(searchMenuExpanded)
|
||||||
|
SearchBottomSheet { searchMenuExpanded =! searchMenuExpanded }
|
||||||
|
|
||||||
|
if(isLongPressedStudent != null)
|
||||||
|
LongPressMenu(isLongPressedStudent!!) { isLongPressedStudent = null }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,74 +0,0 @@
|
|||||||
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.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.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import cafe.adriel.voyager.core.screen.Screen
|
|
||||||
import cafe.adriel.voyager.hilt.getScreenModel
|
|
||||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
|
||||||
import ru.megboyzz.lks.profile.ui.model.SignInScreenModel
|
|
||||||
|
|
||||||
class SignInScreen : Screen {
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
override fun Content() {
|
|
||||||
|
|
||||||
val navigator = LocalNavigator.current!!
|
|
||||||
|
|
||||||
val viewModel = getScreenModel<SignInScreenModel, SignInScreenModel.Factory> { assistedFactory ->
|
|
||||||
assistedFactory.create(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 ?: "Ошибка")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package ru.megboyzz.lks.profile.ui.screens
|
||||||
|
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
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.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import ru.megboyzz.lks.profile.ui.components.LoadingShield
|
||||||
|
import ru.megboyzz.lks.profile.ui.model.SignInViewModel
|
||||||
|
import ru.megboyzz.lks.profile.ui.provider.LocalMainActivity
|
||||||
|
import ru.megboyzz.lks.profile.ui.theme.MainTheme
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SignInScreenContent() = MainTheme {
|
||||||
|
|
||||||
|
val viewModel by LocalMainActivity.current.viewModels<SignInViewModel>()
|
||||||
|
|
||||||
|
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
|
||||||
|
) {
|
||||||
|
|
||||||
|
when(state) {
|
||||||
|
is SignInViewModel.SignInState.Loading, SignInViewModel.SignInState.Success -> LoadingShield("Проверка твоих куки файлов :-)")
|
||||||
|
else -> {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = username,
|
||||||
|
onValueChange = { viewModel.setUsername(it) }
|
||||||
|
)
|
||||||
|
|
||||||
|
OutlinedTextField(
|
||||||
|
value = password,
|
||||||
|
onValueChange = { viewModel.setPassword(it) }
|
||||||
|
)
|
||||||
|
|
||||||
|
OutlinedButton(
|
||||||
|
onClick = viewModel::signIn
|
||||||
|
) {
|
||||||
|
Text("Войти")
|
||||||
|
}
|
||||||
|
|
||||||
|
when (state) {
|
||||||
|
is SignInViewModel.SignInState.Idle -> Text("")
|
||||||
|
is SignInViewModel.SignInState.Success -> Text(state.status ?: "")
|
||||||
|
is SignInViewModel.SignInState.Loading -> CircularProgressIndicator()
|
||||||
|
is SignInViewModel.SignInState.Error -> Text(state.status ?: "Ошибка")
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package ru.megboyzz.lks.profile.ui.screens.sub
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import ru.megboyzz.lks.profile.domain.entities.LksStudent
|
||||||
|
import ru.megboyzz.lks.profile.ui.components.LongPressCardContextMenu
|
||||||
|
import ru.megboyzz.lks.profile.ui.copyToClipboard
|
||||||
|
import ru.megboyzz.lks.profile.ui.goToVK
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun LongPressMenu(
|
||||||
|
lksStudent: LksStudent,
|
||||||
|
onDismiss: () -> Unit
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
val sheetState = rememberModalBottomSheetState()
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
ModalBottomSheet(
|
||||||
|
sheetState = sheetState,
|
||||||
|
onDismissRequest = onDismiss
|
||||||
|
) {
|
||||||
|
LongPressCardContextMenu(
|
||||||
|
lksStudent = lksStudent,
|
||||||
|
onCopyName = context::copyToClipboard,
|
||||||
|
onCopyGroup = context::copyToClipboard,
|
||||||
|
onGoToVk = {
|
||||||
|
Toast.makeText(context, "Переход на VK", Toast.LENGTH_SHORT).show()
|
||||||
|
|
||||||
|
val searchString = lksStudent
|
||||||
|
.studentFullName
|
||||||
|
?.split(" ")
|
||||||
|
?.dropLast(1)
|
||||||
|
?.joinToString(" ") ?: ""
|
||||||
|
|
||||||
|
context.goToVK(searchString)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package ru.megboyzz.lks.profile.ui.screens.sub
|
||||||
|
|
||||||
|
import android.view.ViewTreeObserver
|
||||||
|
import androidx.activity.viewModels
|
||||||
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.ime
|
||||||
|
import androidx.compose.material3.BottomSheetDefaults
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalView
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import ru.megboyzz.lks.profile.ui.components.SearchContextMenu
|
||||||
|
import ru.megboyzz.lks.profile.ui.model.SearchViewModel
|
||||||
|
import ru.megboyzz.lks.profile.ui.provider.LocalMainActivity
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun SearchBottomSheet(onDismiss: () -> Unit) {
|
||||||
|
|
||||||
|
val viewModel by LocalMainActivity.current.viewModels<SearchViewModel>()
|
||||||
|
val request by viewModel.request.collectAsState()
|
||||||
|
|
||||||
|
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
val view = LocalView.current
|
||||||
|
var isImeVisible by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
// Observe keyboard visibility using ViewTreeObserver
|
||||||
|
DisposableEffect(Unit) {
|
||||||
|
val listener = ViewTreeObserver.OnPreDrawListener {
|
||||||
|
// Check if the IME (keyboard) is visible
|
||||||
|
isImeVisible = ViewCompat.getRootWindowInsets(view)
|
||||||
|
?.isVisible(WindowInsetsCompat.Type.ime()) == true
|
||||||
|
true
|
||||||
|
}
|
||||||
|
view.viewTreeObserver.addOnPreDrawListener(listener)
|
||||||
|
onDispose {
|
||||||
|
view.viewTreeObserver.removeOnPreDrawListener(listener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ModalBottomSheet(
|
||||||
|
sheetState = sheetState,
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
//windowInsets = WindowInsets.ime,
|
||||||
|
dragHandle = { BottomSheetDefaults.DragHandle() },
|
||||||
|
modifier = Modifier.then(
|
||||||
|
if (isImeVisible)
|
||||||
|
Modifier.fillMaxHeight(1.0F) // Full height when keyboard is visible
|
||||||
|
else
|
||||||
|
Modifier.fillMaxHeight(0.73F) // Partial height when keyboard is hidden
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
SearchContextMenu(request) { lksRequest ->
|
||||||
|
viewModel.newSearch(lksRequest)
|
||||||
|
onDismiss()
|
||||||
|
coroutineScope.launch { sheetState.hide() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -8,50 +8,48 @@ import androidx.compose.material3.dynamicDarkColorScheme
|
|||||||
import androidx.compose.material3.dynamicLightColorScheme
|
import androidx.compose.material3.dynamicLightColorScheme
|
||||||
import androidx.compose.material3.lightColorScheme
|
import androidx.compose.material3.lightColorScheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
|
||||||
private val DarkColorScheme = darkColorScheme(
|
private val DarkColorScheme = darkColorScheme(
|
||||||
primary = Purple80,
|
primary = Color(0xFFB0BEC5), // Серый с голубым оттенком
|
||||||
secondary = PurpleGrey80,
|
secondary = Color(0xFF90A4AE), // Более мягкий серо-голубой
|
||||||
tertiary = Pink80
|
tertiary = Color(0xFFB39DDB), // Нежный фиолетовый
|
||||||
|
|
||||||
|
background = Color(0xFF121212), // Темный фон для контраста
|
||||||
|
surface = Color(0xFF1E1E1E), // Темная поверхность
|
||||||
|
onPrimary = Color.Black,
|
||||||
|
onSecondary = Color.Black,
|
||||||
|
onTertiary = Color.Black,
|
||||||
|
onBackground = Color(0xFFECEFF1), // Светлый текст на темном фоне
|
||||||
|
onSurface = Color(0xFFECEFF1) // Светлый текст на поверхностях
|
||||||
)
|
)
|
||||||
|
|
||||||
private val LightColorScheme = lightColorScheme(
|
private val LightColorScheme = lightColorScheme(
|
||||||
primary = Purple40,
|
primary = Color(0xFF207AEE), // Голубой с пастельным оттенком
|
||||||
secondary = PurpleGrey40,
|
secondary = Color(0xFFB3E5FC), // Мягкий голубой
|
||||||
tertiary = Pink40
|
tertiary = Color(0xFFC5CAE9), // Светлый фиолетовый
|
||||||
|
|
||||||
/* Other default colors to override
|
background = Color(0xFFF5F5F5), // Очень светлый фон
|
||||||
background = Color(0xFFFFFBFE),
|
surface = Color(0xFFFFFFFF), // Чисто белая поверхность
|
||||||
surface = Color(0xFFFFFBFE),
|
|
||||||
onPrimary = Color.White,
|
onPrimary = Color.White,
|
||||||
onSecondary = Color.White,
|
onSecondary = Color.Black,
|
||||||
onTertiary = Color.White,
|
onTertiary = Color.Black,
|
||||||
onBackground = Color(0xFF1C1B1F),
|
onBackground = Color(0xFF212121), // Темный текст на светлом фоне
|
||||||
onSurface = Color(0xFF1C1B1F),
|
onSurface = Color(0xFF212121) // Темный текст на поверхностях
|
||||||
*/
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MainTheme(
|
fun MainTheme(
|
||||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||||
// Dynamic color is available on Android 12+
|
dynamicColor: Boolean = false, // Отключаем динамические цвета для четкости палитры
|
||||||
dynamicColor: Boolean = true,
|
|
||||||
content: @Composable () -> Unit
|
content: @Composable () -> Unit
|
||||||
) {
|
) {
|
||||||
val colorScheme = when {
|
val colorScheme = LightColorScheme
|
||||||
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(
|
MaterialTheme(
|
||||||
colorScheme = colorScheme,
|
colorScheme = colorScheme,
|
||||||
typography = Typography,
|
typography = Typography,
|
||||||
content = content
|
content = content
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ val Typography = Typography(
|
|||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
lineHeight = 24.sp,
|
lineHeight = 24.sp,
|
||||||
letterSpacing = 0.5.sp
|
letterSpacing = 0.5.sp
|
||||||
)
|
),
|
||||||
/* Other default text styles to override
|
// Other default text styles to override
|
||||||
titleLarge = TextStyle(
|
titleLarge = TextStyle(
|
||||||
fontFamily = FontFamily.Default,
|
fontFamily = FontFamily.Default,
|
||||||
fontWeight = FontWeight.Normal,
|
fontWeight = FontWeight.Medium,
|
||||||
fontSize = 22.sp,
|
fontSize = 22.sp,
|
||||||
lineHeight = 28.sp,
|
lineHeight = 28.sp,
|
||||||
letterSpacing = 0.sp
|
letterSpacing = 0.sp
|
||||||
@@ -30,5 +30,4 @@ val Typography = Typography(
|
|||||||
lineHeight = 16.sp,
|
lineHeight = 16.sp,
|
||||||
letterSpacing = 0.5.sp
|
letterSpacing = 0.5.sp
|
||||||
)
|
)
|
||||||
*/
|
|
||||||
)
|
)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="960"
|
||||||
|
android:viewportHeight="960">
|
||||||
|
<path
|
||||||
|
android:pathData="M440,840v-240h80v80h320v80L520,760v80h-80ZM120,760v-80h240v80L120,760ZM280,600v-80L120,520v-80h160v-80h80v240h-80ZM440,520v-80h400v80L440,520ZM600,360v-240h80v80h160v80L680,280v80h-80ZM120,280v-80h400v80L120,280Z"
|
||||||
|
android:fillColor="#e8eaed"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:width="48dp"
|
||||||
|
android:height="48dp"
|
||||||
|
android:viewportWidth="48"
|
||||||
|
android:viewportHeight="48">
|
||||||
|
<path
|
||||||
|
android:pathData="M42,37c0,2.762 -2.238,5 -5,5H11c-2.761,0 -5,-2.238 -5,-5V11c0,-2.762 2.239,-5 5,-5h26c2.762,0 5,2.238 5,5V37z"
|
||||||
|
android:fillColor="#1976d2"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M35.937,18.041c0.046,-0.151 0.068,-0.291 0.062,-0.416C35.984,17.263 35.735,17 35.149,17h-2.618c-0.661,0 -0.966,0.4 -1.144,0.801c0,0 -1.632,3.359 -3.513,5.574c-0.61,0.641 -0.92,0.625 -1.25,0.625C26.447,24 26,23.786 26,23.199v-5.185C26,17.32 25.827,17 25.268,17h-4.649C20.212,17 20,17.32 20,17.641c0,0.667 0.898,0.827 1,2.696v3.623C21,24.84 20.847,25 20.517,25c-0.89,0 -2.642,-3 -3.815,-6.932C16.448,17.294 16.194,17 15.533,17h-2.643C12.127,17 12,17.374 12,17.774c0,0.721 0.6,4.619 3.875,9.101C18.25,30.125 21.379,32 24.149,32c1.678,0 1.85,-0.427 1.85,-1.094v-2.972C26,27.133 26.183,27 26.717,27c0.381,0 1.158,0.25 2.658,2c1.73,2.018 2.044,3 3.036,3h2.618c0.608,0 0.957,-0.255 0.971,-0.75c0.003,-0.126 -0.015,-0.267 -0.056,-0.424c-0.194,-0.576 -1.084,-1.984 -2.194,-3.326c-0.615,-0.743 -1.222,-1.479 -1.501,-1.879C32.062,25.36 31.991,25.176 32,25c0.009,-0.185 0.105,-0.361 0.249,-0.607C32.223,24.393 35.607,19.642 35.937,18.041z"
|
||||||
|
android:fillColor="#fff"
|
||||||
|
tools:ignore="VectorPath" />
|
||||||
|
</vector>
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 982 B After Width: | Height: | Size: 982 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB |
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
Sample backup rules file; uncomment and customize as necessary.
|
||||||
|
See https://developer.android.com/guide/topics/data/autobackup
|
||||||
|
for details.
|
||||||
|
Note: This file is ignored for devices older that API 31
|
||||||
|
See https://developer.android.com/about/versions/12/backup-restore
|
||||||
|
-->
|
||||||
|
<full-backup-content>
|
||||||
|
<!--
|
||||||
|
<include domain="sharedpref" path="."/>
|
||||||
|
<exclude domain="sharedpref" path="device.xml"/>
|
||||||
|
-->
|
||||||
|
</full-backup-content>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
|
Sample data extraction rules file; uncomment and customize as necessary.
|
||||||
|
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
||||||
|
for details.
|
||||||
|
-->
|
||||||
|
<data-extraction-rules>
|
||||||
|
<cloud-backup>
|
||||||
|
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
||||||
|
<include .../>
|
||||||
|
<exclude .../>
|
||||||
|
-->
|
||||||
|
</cloud-backup>
|
||||||
|
<!--
|
||||||
|
<device-transfer>
|
||||||
|
<include .../>
|
||||||
|
<exclude .../>
|
||||||
|
</device-transfer>
|
||||||
|
-->
|
||||||
|
</data-extraction-rules>
|
||||||
@@ -5,5 +5,6 @@ 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.51.1" apply false
|
id("com.google.devtools.ksp") version "2.2.0-2.0.2" apply false
|
||||||
|
id("com.google.dagger.hilt.android") version "2.57" apply false
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,25 @@
|
|||||||
[versions]
|
[versions]
|
||||||
agp = "8.5.1"
|
agp = "8.11.1"
|
||||||
hiltAndroidCompiler = "2.51.1"
|
hiltAndroidCompiler = "2.57"
|
||||||
jsoup = "1.18.1"
|
jsoup = "1.21.1"
|
||||||
kotlin = "2.0.0"
|
kotlin = "2.2.0"
|
||||||
coreKtx = "1.13.1"
|
coreKtx = "1.16.0"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
junitVersion = "1.2.1"
|
junitVersion = "1.3.0"
|
||||||
espressoCore = "3.6.1"
|
espressoCore = "3.7.0"
|
||||||
lifecycleRuntimeKtx = "2.8.4"
|
lifecycleRuntimeKtx = "2.9.2"
|
||||||
activityCompose = "1.9.1"
|
activityCompose = "1.10.1"
|
||||||
composeBom = "2024.04.01"
|
composeBom = "2025.07.00"
|
||||||
jetbrainsKotlinJvm = "2.0.0"
|
jetbrainsKotlinJvm = "2.2.0"
|
||||||
appcompat = "1.7.0"
|
composeNavigation = "2.9.3"
|
||||||
material = "1.12.0"
|
room_version = "2.7.2"
|
||||||
voyagerVersion = "1.1.0-beta02"
|
serialization = "1.9.0"
|
||||||
|
foundationLayoutAndroid = "1.8.3"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
|
androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room_version" }
|
||||||
|
androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room_version" }
|
||||||
|
androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room_version" }
|
||||||
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 = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroidCompiler" }
|
||||||
hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltAndroidCompiler" }
|
hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltAndroidCompiler" }
|
||||||
@@ -33,13 +37,9 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin
|
|||||||
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||||
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" }
|
navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "composeNavigation" }
|
||||||
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
|
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization"}
|
||||||
voyager-bottom-sheet-navigator = { module = "cafe.adriel.voyager:voyager-bottom-sheet-navigator", version.ref = "voyagerVersion" }
|
androidx-foundation-layout-android = { group = "androidx.compose.foundation", name = "foundation-layout-android", version.ref = "foundationLayoutAndroid" }
|
||||||
voyager-hilt = { module = "cafe.adriel.voyager:voyager-hilt", version.ref = "voyagerVersion" }
|
|
||||||
voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyagerVersion" }
|
|
||||||
voyager-screenmodel = { module = "cafe.adriel.voyager:voyager-screenmodel", version.ref = "voyagerVersion" }
|
|
||||||
voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", version.ref = "voyagerVersion" }
|
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
@@ -47,4 +47,5 @@ kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
|||||||
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||||
jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "jetbrainsKotlinJvm" }
|
jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "jetbrainsKotlinJvm" }
|
||||||
android-library = { id = "com.android.library", version.ref = "agp" }
|
android-library = { id = "com.android.library", version.ref = "agp" }
|
||||||
|
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#Wed Aug 21 19:37:41 MSK 2024
|
#Fri Aug 01 15:44:12 MSK 2025
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
@@ -21,5 +21,3 @@ dependencyResolutionManagement {
|
|||||||
|
|
||||||
rootProject.name = "LKS PROFILE"
|
rootProject.name = "LKS PROFILE"
|
||||||
include(":app")
|
include(":app")
|
||||||
include(":app:domain")
|
|
||||||
include(":app:data")
|
|
||||||
|
|||||||