commit fcc3f992dbd52fcb0e2258c313c5eca1cf850cc2 Author: StepanovPlaton Date: Sun Sep 8 15:08:18 2024 +0400 Initial commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b589d56 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..b3bb7eb --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,18 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..0897082 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..6806f5a --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,53 @@ + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..6d0ee1c --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..0ad17cb --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..50446f4 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,85 @@ +import com.android.build.api.variant.BuildConfigField + +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.compose.compiler) +} + +android { + namespace = "com.example.ssau_schedule" + compileSdk = 34 + + defaultConfig { + applicationId = "com.example.ssau_schedule" + minSdk = 24 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary = true + } + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + androidComponents { + onVariants { + it.buildConfigFields.putAll(mapOf( + Pair("BASE_URL", BuildConfigField("String", "\"https://lk.ssau.ru/\"", null)), + Pair("SIGN_IN_URL", BuildConfigField("String", "\"account/login\"", null)) + )) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } + buildFeatures { + compose = true + buildConfig = true + } + composeOptions { + kotlinCompilerExtensionVersion = "1.5.1" + } + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } +} + +dependencies { + + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.ui) + implementation(libs.androidx.ui.graphics) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.material3) + implementation(libs.androidx.media3.common) + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(platform(libs.androidx.compose.bom)) + androidTestImplementation(libs.androidx.ui.test.junit4) + debugImplementation(libs.androidx.ui.tooling) + debugImplementation(libs.androidx.ui.test.manifest) + + implementation(libs.squareup.okhttp) + implementation(libs.androidx.datastore) +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/com/example/ssau_schedule/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/example/ssau_schedule/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..457cd03 --- /dev/null +++ b/app/src/androidTest/java/com/example/ssau_schedule/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.example.ssau_schedule + +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("com.example.ssau_schedule", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..228170f --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/ssau_schedule/AuthActivity.kt b/app/src/main/java/com/example/ssau_schedule/AuthActivity.kt new file mode 100644 index 0000000..acc7055 --- /dev/null +++ b/app/src/main/java/com/example/ssau_schedule/AuthActivity.kt @@ -0,0 +1,205 @@ +package com.example.ssau_schedule + +import android.content.Intent +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.spring +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.FilledTonalButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import com.example.ssau_schedule.ui.theme.SSAU_ScheduleTheme +import kotlinx.coroutines.delay +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.BiasAlignment +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.core.view.WindowCompat +import com.example.ssau_schedule.api.AuthErrorMessage +import com.example.ssau_schedule.api.AuthorizationAPI +import com.example.ssau_schedule.api.Http +import kotlin.math.min + +class AuthActivity : ComponentActivity() { + private val http = Http() + private var auth: AuthorizationAPI? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + enableEdgeToEdge() + + auth = AuthorizationAPI.getInstance(http, applicationContext) + WindowCompat.setDecorFitsSystemWindows(window, false) + + setContent { + SSAU_ScheduleTheme { + AuthPage() + } + } + } + + @Composable + fun AuthPage() { + var loginOpen by remember { mutableStateOf(false) } + val keyboardOpen by Utils.keyboardState() + val logoHeight by animateFloatAsState( + if (keyboardOpen) 0f else min(LocalConfiguration.current.screenWidthDp, 500) / 101f * 48f, + label = "alpha", + animationSpec = spring( + dampingRatio = Spring.DampingRatioMediumBouncy, + stiffness = Spring.StiffnessLow + )) + + + LaunchedEffect(false) { + delay(1000) + loginOpen = true + } + + Box(Modifier.background(MaterialTheme.colorScheme.primary) + .fillMaxSize().statusBarsPadding().navigationBarsPadding().imePadding(), + contentAlignment = BiasAlignment(0f, -0.25f), + ) { + + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Box(Modifier.widthIn(0.dp, 500.dp) + .height(logoHeight.dp).padding(20.dp, 0.dp) + ) { + Image(painterResource(R.drawable.ssau_logo_01), + contentDescription = stringResource(R.string.samara_university), + modifier = Modifier.fillMaxSize().padding(10.dp), + contentScale = ContentScale.FillWidth, + alignment = Alignment.TopCenter) + } + Box(Modifier.padding(20.dp, 0.dp).widthIn(0.dp, 400.dp)) { + Card(Modifier.fillMaxWidth().animateContentSize(animationSpec = spring( + dampingRatio = Spring.DampingRatioLowBouncy, + stiffness = 50f + )) + .height(if(loginOpen) 280.dp else 0.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.background, + ), + elevation = CardDefaults.cardElevation(defaultElevation = 10.dp) + ) { + AuthForm() + } + } + } + } + } + + @Composable + fun AuthForm() { + val authScope = rememberCoroutineScope() + var login by remember { mutableStateOf("") } + var password by remember { mutableStateOf("") } + var error by remember { mutableStateOf(null) } + + Column(Modifier.fillMaxWidth().padding(30.dp, 20.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + stringResource(R.string.sign_in), + modifier = Modifier.fillMaxWidth(), + color = MaterialTheme.colorScheme.primary, + textAlign = TextAlign.Center, + style = MaterialTheme.typography.displaySmall + ) + OutlinedTextField( + modifier = Modifier.fillMaxWidth(), + value = login, + onValueChange = { login = it; error = null }, + label = { Text(stringResource(R.string.login)) }, + placeholder = + { Text(stringResource(R.string.enter_your_login)) }, + ) + Spacer(Modifier.height(2.dp)) + OutlinedTextField( + modifier = Modifier.fillMaxWidth(), + value = password, + onValueChange = { password = it; error = null }, + label = { Text(stringResource(R.string.password)) }, + placeholder = + { Text(stringResource(R.string.enter_your_password)) }, + ) + Spacer(Modifier.height(2.dp)) + Box(Modifier.fillMaxWidth().height(14.dp)) { + this@Column.AnimatedVisibility( + modifier = Modifier.align(Alignment.Center), + visible = error !== null, + enter = fadeIn(animationSpec = spring( + dampingRatio = Spring.DampingRatioMediumBouncy, + stiffness = Spring.StiffnessVeryLow + )), + exit = fadeOut(animationSpec = spring( + dampingRatio = Spring.DampingRatioMediumBouncy, + stiffness = Spring.StiffnessVeryLow + )) + ) { + Text(error?.getMessage(applicationContext) ?: "", + color = MaterialTheme.colorScheme.error, + style = MaterialTheme.typography.labelSmall + ) + } + } + + Spacer(Modifier.height(4.dp)) + FilledTonalButton(onClick = { + if(login.length < 5) error = AuthErrorMessage.LOGIN_IS_TOO_SHORT + else if(password.length < 5) error = AuthErrorMessage.PASSWORD_IS_TOO_SHORT + else { + auth?.signIn(login, password, authScope, + { startActivity(Intent(applicationContext, AuthActivity::class.java)) }, + { _, _ -> + error = AuthErrorMessage.INCORRECT_LOGIN_OR_PASSWORD + } + ) + } + }, + shape = RoundedCornerShape(50), + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.primary) + ) { + Text(stringResource(R.string.sign_in)) + } + } + } +} diff --git a/app/src/main/java/com/example/ssau_schedule/Utils.kt b/app/src/main/java/com/example/ssau_schedule/Utils.kt new file mode 100644 index 0000000..233c75b --- /dev/null +++ b/app/src/main/java/com/example/ssau_schedule/Utils.kt @@ -0,0 +1,34 @@ +package com.example.ssau_schedule + +import android.graphics.Rect +import android.view.ViewTreeObserver +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalView + +class Utils { + companion object { + + @Composable + fun keyboardState(): State { + val keyboardOpen = remember { mutableStateOf(false) } + val view = LocalView.current + DisposableEffect(view) { + val onGlobalListener = ViewTreeObserver.OnGlobalLayoutListener { + val rect = Rect() + view.getWindowVisibleDisplayFrame(rect) + val screenHeight = view.rootView.height + val keypadHeight = screenHeight - rect.bottom + keyboardOpen.value = keypadHeight > screenHeight * 0.15 + } + view.viewTreeObserver.addOnGlobalLayoutListener(onGlobalListener) + onDispose { view.viewTreeObserver.removeOnGlobalLayoutListener(onGlobalListener) } + } + return keyboardOpen + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/ssau_schedule/api/Auth.kt b/app/src/main/java/com/example/ssau_schedule/api/Auth.kt new file mode 100644 index 0000000..91d1d47 --- /dev/null +++ b/app/src/main/java/com/example/ssau_schedule/api/Auth.kt @@ -0,0 +1,87 @@ +package com.example.ssau_schedule.api + +import android.annotation.SuppressLint +import android.content.Context +import androidx.datastore.preferences.core.edit +import com.example.ssau_schedule.BuildConfig +import com.example.ssau_schedule.R +import com.example.ssau_schedule.data.source.AuthStoreKeys +import com.example.ssau_schedule.data.source.authStore +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import okhttp3.Headers.Companion.toHeaders +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.Response +import okio.IOException +import org.json.JSONArray + +enum class AuthErrorMessage(private val resource: Int) { + LOGIN_IS_TOO_SHORT(R.string.login_is_too_short), + PASSWORD_IS_TOO_SHORT(R.string.password_is_too_short), + INCORRECT_LOGIN_OR_PASSWORD(R.string.incorrect_login_or_password); + + fun getMessage(context: Context) = + context.getString(resource) +} + +class AuthorizationAPI(private var http: Http, private var context: Context) { + companion object { + @SuppressLint("StaticFieldLeak") + @Volatile + private var INSTANCE: AuthorizationAPI? = null + fun getInstance(http: Http, context: Context) = + INSTANCE ?: synchronized(this) { + INSTANCE ?: AuthorizationAPI(http, context).also { + INSTANCE = it + } + } + } + + private val responseHasAuthToken = + {response: Response? -> response?.headers?.toMap()?.containsKey("set-cookie") == true} + + private fun safeAuthToken(response: Response, + authScope: CoroutineScope, + callback: HttpResponseCallback) { + authScope.launch { + context.authStore.edit { authStore -> + authStore[AuthStoreKeys.AUTH_TOKEN] = + response.headers("set-cookie").joinToString(", ") + }.run { + callback(response) + } + } + } + + fun signIn(login: String, password: String, authScope: CoroutineScope, + callback: HttpResponseCallback, + exceptionCallback: HttpExceptionCallback? = null) { + http.request( + Method.POST, + BuildConfig.SIGN_IN_URL, + JSONArray(arrayOf(mapOf( + Pair("login", login), + Pair("password", password) + ))).toString().toRequestBody("application/json".toMediaType()), + mapOf( + Pair("Next-Action", "b395d17834d8b7df06372cbf1f241170a272d540") + ).toHeaders(), + fun(response) { + if(responseHasAuthToken(response)) + safeAuthToken(response, authScope, callback) + else if(exceptionCallback != null) + exceptionCallback(IOException("Authorization token not found"), response) + }, + fun (error, response): Boolean { + if(responseHasAuthToken(response)) return true + if(exceptionCallback !== null) exceptionCallback(error, response) + return false + } + ) + } + + + + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/ssau_schedule/api/Http.kt b/app/src/main/java/com/example/ssau_schedule/api/Http.kt new file mode 100644 index 0000000..c9b046f --- /dev/null +++ b/app/src/main/java/com/example/ssau_schedule/api/Http.kt @@ -0,0 +1,54 @@ +package com.example.ssau_schedule.api + +import android.util.Log +import com.example.ssau_schedule.BuildConfig +import okhttp3.Call +import okhttp3.Callback +import okhttp3.Headers +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.RequestBody +import okhttp3.Response +import java.io.IOException + +enum class Method { + GET, + POST, + PUT, + DELETE +} + +typealias HttpResponseCallback = (response: Response) -> Unit +typealias HttpExceptionVerifyCallback = (exception: IOException, response: Response?) -> Boolean +typealias HttpExceptionCallback = (exception: IOException, response: Response?) -> Unit + +class Http { + val http = OkHttpClient() + + fun request( + method: Method, + url: String, + body: RequestBody? = null, + headers: Headers? = null, + callback: HttpResponseCallback, + exceptionCallback: HttpExceptionVerifyCallback? = null + ) { + val request = Request.Builder().url(BuildConfig.BASE_URL+url). + method(method.toString(), body) + if(headers !== null) request.headers(headers) + http.newCall(request.build()).enqueue(object : Callback { + override fun onFailure(call: Call, e: IOException) { + Log.e("Http request failed", e.toString()) + if(exceptionCallback !== null) exceptionCallback(e, null) + } + + override fun onResponse(call: Call, response: Response) { + var runCallback = false + if (!response.isSuccessful && exceptionCallback !== null) + runCallback = exceptionCallback( + IOException("Http response is not successful"), response) + if(runCallback || response.isSuccessful) callback(response) + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/ssau_schedule/data/source/Auth.kt b/app/src/main/java/com/example/ssau_schedule/data/source/Auth.kt new file mode 100644 index 0000000..5f57ae2 --- /dev/null +++ b/app/src/main/java/com/example/ssau_schedule/data/source/Auth.kt @@ -0,0 +1,11 @@ +package com.example.ssau_schedule.data.source + +import android.content.Context +import androidx.datastore.preferences.core.stringPreferencesKey +import androidx.datastore.preferences.preferencesDataStore + +val Context.authStore by preferencesDataStore(name = "auth") + +object AuthStoreKeys { + val AUTH_TOKEN = stringPreferencesKey("auth_token") +} \ No newline at end of file diff --git a/app/src/main/java/com/example/ssau_schedule/ui/theme/Color.kt b/app/src/main/java/com/example/ssau_schedule/ui/theme/Color.kt new file mode 100644 index 0000000..4ae8aa2 --- /dev/null +++ b/app/src/main/java/com/example/ssau_schedule/ui/theme/Color.kt @@ -0,0 +1,18 @@ +package com.example.ssau_schedule.ui.theme + +import androidx.compose.ui.graphics.Color + +val Primary01 = Color(0xFF0D47A1) +val Primary02 = Color(0xFF134BC5) +val Primary03 = Color(0xFF1D5DEB) +val Primary04 = Color(0xFF2780E3) +val Primary05 = Color(0xFF6B92E5) +val Primary06 = Color(0xFFA8C4EC) + +val White = Color(0xFFFFFFFF) + +val Gray01 = Color(0xFF2C2C2C) +val Gray02 = Color(0xFF383838) +val Gray03 = Color(0xFF66727F) + +val Red = Color(0xFFEE3F58) \ No newline at end of file diff --git a/app/src/main/java/com/example/ssau_schedule/ui/theme/Theme.kt b/app/src/main/java/com/example/ssau_schedule/ui/theme/Theme.kt new file mode 100644 index 0000000..fbe7361 --- /dev/null +++ b/app/src/main/java/com/example/ssau_schedule/ui/theme/Theme.kt @@ -0,0 +1,64 @@ +package com.example.ssau_schedule.ui.theme + +import android.app.Activity +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +private val DarkColorScheme = darkColorScheme( + primary = Primary01, + secondary = Primary03, + tertiary = Primary04, + background = Gray01, + surface = Gray02, + error = Red, +) + +private val LightColorScheme = lightColorScheme( + primary = Primary01, + secondary = Primary03, + tertiary = Primary04, + background = White, + surface = Primary06, + error = Red, + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +@Composable +fun SSAU_ScheduleTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + //dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { +// dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { +// val context = LocalContext.current +// if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) +// } + + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/ssau_schedule/ui/theme/Type.kt b/app/src/main/java/com/example/ssau_schedule/ui/theme/Type.kt new file mode 100644 index 0000000..5421b52 --- /dev/null +++ b/app/src/main/java/com/example/ssau_schedule/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package com.example.ssau_schedule.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.SansSerif, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ), + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..4ba0bac --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..871140a --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ssau_logo_01.xml b/app/src/main/res/drawable/ssau_logo_01.xml new file mode 100644 index 0000000..967e01d --- /dev/null +++ b/app/src/main/res/drawable/ssau_logo_01.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml new file mode 100644 index 0000000..6448f45 --- /dev/null +++ b/app/src/main/res/values-en/strings.xml @@ -0,0 +1,13 @@ + + + SSAU Schedule + Sign in + Login + Enter your login + Enter your password + Password + Samara university + Incorrect login or password + Login is too short + Password is too short + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/app/src/main/res/values/endpoints.xml b/app/src/main/res/values/endpoints.xml new file mode 100644 index 0000000..8b30f16 --- /dev/null +++ b/app/src/main/res/values/endpoints.xml @@ -0,0 +1,4 @@ + + + Расписание СамГУ + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..1bae4a6 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,12 @@ + + Расписание СамГУ + Войти + Логин + Введите ваш логин + Введите ваш пароль + Пароль + Самарский университет + Неверный логин или пароль + Логин слишком короткий + Пароль слишком короткий + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..55631a7 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ + + + +