commit a3ef2c1f942030953dbbd129bcb0682dfd9cc3e5 Author: Сергей Полевой Date: Mon Nov 20 22:30:17 2023 +0400 Лабораторная работа №2: готова 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 b/app/build.gradle new file mode 100644 index 0000000..76c65d4 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,65 @@ +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' +} + +android { + namespace 'com.example.dtf' + compileSdk 34 + + defaultConfig { + applicationId "com.example.dtf" + minSdk 23 + targetSdk 34 + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary true + } + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } + buildFeatures { + compose true + } + composeOptions { + kotlinCompilerExtensionVersion '1.2.0' + } + packagingOptions { + resources { + excludes += '/META-INF/{AL2.0,LGPL2.1}' + } + } +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.20" + def nav_version = "2.7.0" + implementation "androidx.navigation:navigation-compose:$nav_version" + implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' + implementation 'androidx.activity:activity-compose:1.3.1' + implementation "androidx.compose.ui:ui:$compose_ui_version" + implementation "androidx.compose.ui:ui-tooling-preview:$compose_ui_version" + implementation 'androidx.compose.material:material:1.2.0' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' + androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_ui_version" + debugImplementation "androidx.compose.ui:ui-tooling:$compose_ui_version" + debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_ui_version" +} \ 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/dtf/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/example/dtf/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..4d61534 --- /dev/null +++ b/app/src/androidTest/java/com/example/dtf/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.example.dtf + +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.shawarma", 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..9015826 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/MainActivity.kt b/app/src/main/java/com/example/dtf/MainActivity.kt new file mode 100644 index 0000000..e5f7fee --- /dev/null +++ b/app/src/main/java/com/example/dtf/MainActivity.kt @@ -0,0 +1,129 @@ +package com.example.dtf + +import android.annotation.SuppressLint +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.Scaffold +import androidx.compose.material.Text +import androidx.compose.material.TopAppBar +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.currentBackStackEntryAsState +import androidx.navigation.compose.rememberNavController +import androidx.navigation.navigation +import com.example.dtf.models.currentUser +import com.example.dtf.screens.EditPostScreen +import com.example.dtf.screens.LoginScreen +import com.example.dtf.screens.NewPostScreen +import com.example.dtf.screens.PostScreen +import com.example.dtf.screens.PostsScreen +import com.example.dtf.screens.ProfileScreen +import com.example.dtf.screens.RegisterScreen +import com.example.dtf.utils.ScreenPaths +import com.example.dtf.widgets.BottomNavBar + +class MainActivity : ComponentActivity() { + @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter", + "UnusedMaterialScaffoldPaddingParameter" + ) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { + val navController = rememberNavController() + val navBackStackEntry by navController.currentBackStackEntryAsState() + val currentRoute = navBackStackEntry?.destination?.route + + val includeBackButton = remember { mutableStateOf(false) } + + Scaffold( + modifier = Modifier.fillMaxSize(), + backgroundColor = Color.Transparent, + topBar = { + if (currentRoute != ScreenPaths.Login.route && currentRoute != ScreenPaths.Register.route && includeBackButton.value) { + TopAppBar( + modifier = Modifier.height(60.dp), + title = { + + }, + navigationIcon = { + IconButton( + onClick = { + includeBackButton.value = false + navController.popBackStack() + } + ) { + Icon( + imageVector = Icons.Default.ArrowBack, + contentDescription = "Назад", + ) + } + + }, + ) + } + }, + bottomBar = { + if (currentRoute != ScreenPaths.Login.route && currentRoute != ScreenPaths.Register.route) { + BottomNavBar(navController) + } + } + ) { + NavHost(navController, ScreenPaths.Login.route) { + composable(ScreenPaths.Login.route) { + LoginScreen(navController) + } + composable(ScreenPaths.Register.route) { + RegisterScreen(navController) + } + navigation(ScreenPaths.Profile.route + "/show", ScreenPaths.Profile.route) { + composable(ScreenPaths.Profile.route + "/show") { + includeBackButton.value = false + ProfileScreen(navController) + } + } + navigation(ScreenPaths.Posts.route + "/all", ScreenPaths.Posts.route) { + composable(ScreenPaths.Posts.route + "/all") { + includeBackButton.value = false + PostsScreen(navController) + } + composable(ScreenPaths.NewPost.route) { + includeBackButton.value = true + NewPostScreen() + } + composable(ScreenPaths.Post.route) { navBackStackEntry -> + includeBackButton.value = true + navBackStackEntry.arguments?.getString("post")?.let { postId -> + PostScreen( + postId.toInt(), + navController + ) + } + } + composable(ScreenPaths.EditPost.route) { navBackStackEntry -> + includeBackButton.value = true + navBackStackEntry.arguments?.getString("post")?.let { postId -> + EditPostScreen( + postId.toInt() + ) + } + } + } + } + } + } + } +} + diff --git a/app/src/main/java/com/example/dtf/models/Category.kt b/app/src/main/java/com/example/dtf/models/Category.kt new file mode 100644 index 0000000..5e81eb7 --- /dev/null +++ b/app/src/main/java/com/example/dtf/models/Category.kt @@ -0,0 +1,14 @@ +package com.example.dtf.models + +data class Category( + val id: Int, + val name: String +) + +fun getAllCategories() : List { + return listOf( + Category(1, "Игры"), + Category(2, "Кино"), + Category(3, "Аниме"), + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/models/Comment.kt b/app/src/main/java/com/example/dtf/models/Comment.kt new file mode 100644 index 0000000..ce35473 --- /dev/null +++ b/app/src/main/java/com/example/dtf/models/Comment.kt @@ -0,0 +1,10 @@ +package com.example.dtf.models + +import java.util.Date + +data class Comment( + val id: Int, + val userId: Int, + val content: String, + val date: Date +) diff --git a/app/src/main/java/com/example/dtf/models/Post.kt b/app/src/main/java/com/example/dtf/models/Post.kt new file mode 100644 index 0000000..e9a2fe8 --- /dev/null +++ b/app/src/main/java/com/example/dtf/models/Post.kt @@ -0,0 +1,76 @@ +package com.example.dtf.models + +import androidx.compose.runtime.collection.MutableVector +import androidx.compose.runtime.collection.mutableVectorOf +import java.util.Date + +private val posts = listOf( + Post( + 1, + "Что не так с half-life 2", + "Да всё не так", + 1, + mutableVectorOf( + Comment(1, 2, "Пост бред. Начнём с того, что вот эта твоя манера речи клоунская...", Date(2023, 10, 20)), + Comment(2, 1, "Да какой бред, чел, я всё по факту сказал", Date(2023, 10, 20)) + ), + Date(2023, 10, 22) + ), + Post( + 2, + "Я действительно ненавижу фильм про титаник", + "Пруфов не будет", + 2, + mutableVectorOf(Comment(1, 2, "Очередной бред от автора", Date(2023, 9, 20))), + Date(2023, 9, 22) + ), + Post( + 3, + "\"Госпожа Кагуя\" это переоценённый тайтл", + "Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово", + 3, + mutableVectorOf(Comment(1, 2, "Автора на увольнение", Date(2023, 9, 20))), + Date(2023, 9, 22) + ), + Post( + 4, + "\"Восхождение в тени\" это переоценённый тайтл", + "Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово", + 3, + mutableVectorOf(Comment(1, 2, "Автора на увольнение", Date(2023, 9, 20))), + Date(2023, 9, 22) + ), + Post( + 5, + "\"Тетрадь смерти\" это переоценённый тайтл", + "Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово", + 3, + mutableVectorOf(Comment(1, 2, "Автора на увольнение", Date(2023, 9, 20))), + Date(2023, 9, 22) + ), + Post( + 6, + "\"Бакуман\" это переоценённый тайтл", + "Я, конечно, не смотрел, но мне так кажется. А всё потому, что там происходит такое, что даже Аристотелю не снилось. А может даже Платону. Об этом можно рассуждать тысячи лет, но я смогу уложиться всего в пару слов. И первое слово - этот тайтл полное днище. Ладно, не слово", + 3, + mutableVectorOf(Comment(1, 2, "Автора на увольнение", Date(2023, 9, 20))), + Date(2023, 9, 22) + ) +) + +data class Post( + val id: Int, + var title: String, + var content: String, + val categoryId: Int, + val comments: MutableVector, + val date: Date, +) + +fun getAllPosts() : List { + return posts +} + +fun getPostsByCategory(categoryId: Int) : List { + return posts.filter { it.categoryId == categoryId } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/models/User.kt b/app/src/main/java/com/example/dtf/models/User.kt new file mode 100644 index 0000000..5f33ec5 --- /dev/null +++ b/app/src/main/java/com/example/dtf/models/User.kt @@ -0,0 +1,19 @@ +package com.example.dtf.models + +data class User( + val id: Int, + val username: String, + val password: String, + val isModerator: Boolean +) + +fun getAllUsers() : List { + return listOf( + User(1, "Sheriff", "123456", true), + User(2, "Shailushaika", "654321", false), + ) +} + +fun currentUser() : User { + return getAllUsers()[0] +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/screens/EditPostScreen.kt b/app/src/main/java/com/example/dtf/screens/EditPostScreen.kt new file mode 100644 index 0000000..ed8e556 --- /dev/null +++ b/app/src/main/java/com/example/dtf/screens/EditPostScreen.kt @@ -0,0 +1,69 @@ +package com.example.dtf.screens + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.* +import androidx.compose.runtime.* +import androidx.compose.ui.* +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.* +import com.example.dtf.models.getAllCategories +import com.example.dtf.models.getAllPosts +import com.example.dtf.utils.ScreenPaths +import com.example.dtf.widgets.MyTextField + +@Composable +fun EditPostScreen(postId: Int) { + val post = getAllPosts().first {p -> p.id == postId} + + val title = remember { mutableStateOf(TextFieldValue(post.title)) } + val content = remember { mutableStateOf(TextFieldValue(post.content)) } + + Column( + modifier = Modifier.verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(10.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + MyTextField( + value = title, + "Заголовок", + onChanged = {title.value = it}, + modifier = Modifier.fillMaxWidth().padding(15.dp, 10.dp, 15.dp, 0.dp), + backgroundColor = Color(0xFFFEFEFE) + ) + MyTextField( + value = content, + "Содержимое", + onChanged = {content.value = it}, + modifier = Modifier.fillMaxWidth().heightIn(min = 500.dp).padding(horizontal = 15.dp), + backgroundColor = Color(0xFFFEFEFE), + false + ) + Button( + { + + }, + colors = ButtonDefaults.buttonColors(Color(0xFF388DF2)), + border = BorderStroke(1.dp, Color(0xFF0085FF)), + shape = RoundedCornerShape(15.dp), + modifier = Modifier.fillMaxWidth(0.5f) + ) { + Text( + text = "Сохранить", + style = TextStyle( + fontSize = 24.sp, + fontWeight = FontWeight(400), + color = Color(0xFFFFFFFF), + ) + ) + } + Spacer(modifier = Modifier.height(70.dp)) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/screens/LoginScreen.kt b/app/src/main/java/com/example/dtf/screens/LoginScreen.kt new file mode 100644 index 0000000..cd4de29 --- /dev/null +++ b/app/src/main/java/com/example/dtf/screens/LoginScreen.kt @@ -0,0 +1,101 @@ +package com.example.dtf.screens + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.* +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.navigation.NavController +import androidx.navigation.NavHostController +import com.example.dtf.utils.ScreenPaths +import com.example.dtf.widgets.MyTextField + +@Composable +fun LoginScreen(navController: NavHostController) { + val login = remember { mutableStateOf(TextFieldValue(""))} + val password = remember { mutableStateOf(TextFieldValue(""))} + Row( + horizontalArrangement = Arrangement.Center, + modifier = Modifier.fillMaxSize() + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxWidth(0.7f).fillMaxHeight().padding(vertical=100.dp) + ) { + Text( + text = "Авторизация", + style = TextStyle( + fontSize = 40.sp, + fontWeight = FontWeight(400), + color = Color(0xFF000000), + ), + modifier = Modifier.fillMaxHeight(0.3f) + ) + MyTextField( + login, + "Логин", + {login.value = it}, + modifier = Modifier + .padding(bottom=30.dp) + .fillMaxWidth() + .heightIn(max=55.dp) + ) + MyTextField( + password, + "Пароль", + {password.value = it}, + modifier = Modifier + .padding(bottom=60.dp) + .fillMaxWidth() + .heightIn(max=55.dp) + ) + Button( + { + navController.navigate(ScreenPaths.Posts.route) + }, + colors = ButtonDefaults.buttonColors(Color(0xFF388DF2)), + border = BorderStroke(1.dp, Color(0xFF0085FF)), + shape = RoundedCornerShape(15.dp), + modifier = Modifier.padding(bottom=20.dp).fillMaxWidth(0.5f) + ) { + Text( + text = "Войти", + style = TextStyle( + fontSize = 24.sp, + fontWeight = FontWeight(400), + color = Color(0xFFFFFFFF), + ) + ) + } + Text( + text = "Нет аккаунта?", + style = TextStyle( + fontSize = 16.sp, + fontWeight = FontWeight(400), + color = Color(0xFFB6B3B3), + ), + modifier = Modifier.clickable { + navController.navigate(ScreenPaths.Register.route) { + popUpTo(ScreenPaths.Register.route) { + inclusive = true + } + } + } + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/screens/NewPostScreen.kt b/app/src/main/java/com/example/dtf/screens/NewPostScreen.kt new file mode 100644 index 0000000..c31b977 --- /dev/null +++ b/app/src/main/java/com/example/dtf/screens/NewPostScreen.kt @@ -0,0 +1,107 @@ +package com.example.dtf.screens + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.* +import androidx.compose.runtime.* +import androidx.compose.ui.* +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.* +import com.example.dtf.models.getAllCategories +import com.example.dtf.utils.ScreenPaths +import com.example.dtf.widgets.MyTextField + +@Composable +fun NewPostScreen() { + val selectedCategory = remember { mutableStateOf(getAllCategories()[0].name) } + val expanded = remember { mutableStateOf(false) } + + val title = remember { mutableStateOf(TextFieldValue()) } + val content = remember { mutableStateOf(TextFieldValue()) } + + Column( + modifier = Modifier.verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(10.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Button( + { + expanded.value = true + }, + colors = ButtonDefaults.buttonColors(Color(0xFFFFFFFF)), + border = BorderStroke(1.dp, Color(0xFF0085FF)), + shape = RoundedCornerShape(15.dp), + modifier = Modifier.fillMaxWidth().padding(15.dp, 10.dp, 15.dp, 0.dp) + ) { + Text( + text = selectedCategory.value, + style = TextStyle( + fontSize = 24.sp, + fontWeight = FontWeight(400), + color = Color(0xFF000000), + ) + ) + DropdownMenu( + modifier = Modifier.fillMaxWidth().padding(horizontal = 15.dp), + expanded = expanded.value, + onDismissRequest = {expanded.value = false} + ) { + getAllCategories().forEach {category -> + DropdownMenuItem( + onClick = { + selectedCategory.value = category.name + expanded.value = false + }, + ) { + Text( + text = category.name, + fontSize = 16.sp, + textAlign = TextAlign.Center + ) + } + } + } + } + MyTextField( + value = title, + "Заголовок", + onChanged = {title.value = it}, + modifier = Modifier.fillMaxWidth().padding(horizontal = 15.dp), + backgroundColor = Color(0xFFFEFEFE) + ) + MyTextField( + value = content, + "Содержимое", + onChanged = {content.value = it}, + modifier = Modifier.fillMaxWidth().heightIn(min = 500.dp).padding(horizontal = 15.dp), + backgroundColor = Color(0xFFFEFEFE), + false + ) + Button( + { + + }, + colors = ButtonDefaults.buttonColors(Color(0xFF388DF2)), + border = BorderStroke(1.dp, Color(0xFF0085FF)), + shape = RoundedCornerShape(15.dp), + modifier = Modifier.fillMaxWidth(0.5f) + ) { + Text( + text = "Опубликовать", + style = TextStyle( + fontSize = 24.sp, + fontWeight = FontWeight(400), + color = Color(0xFFFFFFFF), + ) + ) + } + Spacer(modifier = Modifier.height(70.dp)) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/screens/PostScreen.kt b/app/src/main/java/com/example/dtf/screens/PostScreen.kt new file mode 100644 index 0000000..0c7ca14 --- /dev/null +++ b/app/src/main/java/com/example/dtf/screens/PostScreen.kt @@ -0,0 +1,175 @@ +package com.example.dtf.screens + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.AddCircle +import androidx.compose.material.icons.filled.ThumbUp +import androidx.compose.runtime.Composable +import androidx.compose.runtime.* +import androidx.compose.ui.* +import androidx.compose.ui.draw.* +import androidx.compose.ui.geometry.* +import androidx.compose.ui.graphics.* +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.* +import androidx.navigation.NavHostController +import com.example.dtf.R +import com.example.dtf.models.Comment +import com.example.dtf.models.currentUser +import com.example.dtf.models.getAllCategories +import com.example.dtf.models.getAllPosts +import com.example.dtf.models.getAllUsers +import com.example.dtf.models.getPostsByCategory +import com.example.dtf.utils.ScreenPaths +import com.example.dtf.widgets.MyTextField + +@Composable +fun PostScreen(postId: Int, navController: NavHostController) { + val post = getAllPosts().first {p -> p.id == postId} + + val content = remember { mutableStateOf(TextFieldValue("")) } + + Column( + modifier = Modifier + .fillMaxSize() + .background(Color.White) + .verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(5.dp), + horizontalAlignment = Alignment.Start + ) { + Text( + modifier = Modifier.padding(10.dp), + text = post.title, + fontSize = 26.sp + ) + Text( + modifier = Modifier.fillMaxHeight().padding(10.dp, 0.dp, 10.dp, 0.dp), + text = post.content, + fontSize = 20.sp + ) + Row( + modifier = Modifier.fillMaxWidth().padding(10.dp), + verticalAlignment = Alignment.Bottom, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = "day.month.year".replace( + "day", + post.date.day.toString() + ).replace( + "month", + post.date.month.toString() + ).replace( + "year", + post.date.year.toString() + ), + fontSize = 14.sp, + color = Color(0xFFCECCCC) + ) + if (currentUser().isModerator) { + Text( + modifier = Modifier.clickable { + navController.navigate(ScreenPaths.EditPost.route.replace("{post}", postId.toString())) + }, + text = "Изменить", + fontSize = 18.sp, + color = Color(0xFFAFAFAF) + ) + } + Row ( + modifier = Modifier, + horizontalArrangement = Arrangement.End + ) { + Text( + text = "0", + fontSize = 16.sp, + color = Color.Green + ) + Icon( + modifier = Modifier.padding(start = 8.dp), + imageVector = Icons.Default.ThumbUp, + contentDescription = null + ) + } + } + Divider() + Text( + modifier = Modifier.fillMaxWidth().padding(0.dp, 0.dp, 0.dp, 10.dp), + text = "Комментарии", + fontSize = 22.sp, + textAlign = TextAlign.Center + ) + Row( + modifier = Modifier.fillMaxSize().padding(horizontal = 15.dp), + verticalAlignment = Alignment.CenterVertically + ) { + MyTextField( + value = content, + placeholder = "Комментарий", + onChanged = {content.value = it}, + modifier = Modifier.fillMaxWidth(0.9f), + backgroundColor = Color.White + ) + IconButton( + modifier = Modifier.fillMaxWidth().scale(1.5f), + onClick = {}, + ) { + Icon( + imageVector = Icons.Default.AddCircle, + contentDescription = null + ) + } + } + post.comments.forEachReversed {comment -> + Comment(comment) + } + Spacer(modifier = Modifier.height(60.dp)) + } +} + +@Composable +private fun Comment(comment: Comment) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 15.dp, vertical = 5.dp) + .border(width = 1.dp, color = Color(0xFFADADAD), shape = RoundedCornerShape(size = 10.dp)) + .background(color = Color(0xFFFFFFFF), shape = RoundedCornerShape(size = 10.dp)) + ) { + Row( + modifier = Modifier.fillMaxWidth().padding(10.dp), + verticalAlignment = Alignment.Top, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = getAllUsers().first {u -> u.id == comment.userId}.username, + fontSize = 20.sp + ) + Text( + text = "day.month.year".replace( + "day", + comment.date.day.toString() + ).replace( + "month", + comment.date.month.toString() + ).replace( + "year", + comment.date.year.toString() + ), + fontSize = 14.sp, + color = Color(0xFFCECCCC) + ) + } + Text( + modifier = Modifier.padding(start = 10.dp, bottom = 5.dp), + text = comment.content, + fontSize = 16.sp + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/screens/PostsScreen.kt b/app/src/main/java/com/example/dtf/screens/PostsScreen.kt new file mode 100644 index 0000000..a51fabe --- /dev/null +++ b/app/src/main/java/com/example/dtf/screens/PostsScreen.kt @@ -0,0 +1,147 @@ +package com.example.dtf.screens + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.* +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ThumbUp +import androidx.compose.runtime.Composable +import androidx.compose.runtime.* +import androidx.compose.ui.* +import androidx.compose.ui.draw.* +import androidx.compose.ui.geometry.* +import androidx.compose.ui.graphics.* +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.* +import androidx.navigation.NavHostController +import com.example.dtf.models.getAllCategories +import com.example.dtf.models.getAllPosts +import com.example.dtf.models.getPostsByCategory +import com.example.dtf.utils.ScreenPaths + +@Composable +fun PostsScreen(navController: NavHostController) { + val currentCategory = remember { mutableStateOf(getAllCategories()[0].name) } + + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.spacedBy(10.dp) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight(0.1f) + .horizontalScroll( + rememberScrollState() + ) + .background(Color.White), + horizontalArrangement = Arrangement.spacedBy(25.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Categories(currentCategory) + } + Row( + modifier = Modifier.fillMaxSize() + ) { + Column( + modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(10.dp) + ) { + PostsByCategory(navController, currentCategory.value) + Spacer(modifier = Modifier.height(60.dp)) + } + } + } +} + +@Composable +private fun Categories(categoryState: MutableState) { + Spacer(modifier = Modifier.width(5.dp)) + getAllCategories().forEach {category -> + Text( + modifier = Modifier + .clickable { + categoryState.value = category.name + } + .drawBehind { + if (category.name == categoryState.value) { + val strokeWidthPx = 2.dp.toPx() + val verticalOffset = size.height + 2.sp.toPx() + drawLine( + color = Color(0xFF319CFF), + strokeWidth = strokeWidthPx, + start = Offset(0f, verticalOffset), + end = Offset(size.width, verticalOffset) + ) + } + }, + text = category.name, + fontSize = 22.sp + ) + } +} + +@Composable +private fun PostsByCategory(navController: NavHostController, category: String) { + getPostsByCategory(getAllCategories().first { c -> c.name == category }.id).forEach { post -> + Column( + modifier = Modifier + .fillMaxHeight(0.3f) + .heightIn(max = 250.dp) + .fillMaxWidth() + .background(Color.White) + .clickable { + navController.navigate(ScreenPaths.Post.route.replace("{post}", post.id.toString())) + }, + verticalArrangement = Arrangement.spacedBy(5.dp), + horizontalAlignment = Alignment.Start + ) { + Text( + modifier = Modifier.padding(10.dp), + text = post.title, + fontSize = 26.sp + ) + Text( + modifier = Modifier.fillMaxHeight(0.6f).padding(10.dp, 0.dp, 10.dp, 0.dp), + text = post.content, + fontSize = 20.sp, + overflow = TextOverflow.Ellipsis + ) + Row( + modifier = Modifier.fillMaxWidth().padding(10.dp), + verticalAlignment = Alignment.Bottom, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = "day.month.year".replace( + "day", + post.date.day.toString() + ).replace( + "month", + post.date.month.toString() + ).replace( + "year", + post.date.year.toString() + ), + fontSize = 14.sp, + color = Color(0xFFCECCCC) + ) + Row ( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End + ) { + Text( + text = "0", + fontSize = 16.sp, + color = Color.Green + ) + Icon( + modifier = Modifier.padding(start = 8.dp), + imageVector = Icons.Default.ThumbUp, + contentDescription = null + ) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/screens/ProfileScreen.kt b/app/src/main/java/com/example/dtf/screens/ProfileScreen.kt new file mode 100644 index 0000000..3154798 --- /dev/null +++ b/app/src/main/java/com/example/dtf/screens/ProfileScreen.kt @@ -0,0 +1,81 @@ +package com.example.dtf.screens + +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.* +import androidx.compose.ui.graphics.* +import androidx.compose.ui.unit.* +import androidx.navigation.NavHostController +import com.example.dtf.models.currentUser +import com.example.dtf.utils.ScreenPaths + +@Composable +fun ProfileScreen(navController: NavHostController) { + Column( + modifier = Modifier.fillMaxSize() + ) { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .fillMaxHeight(0.3f) + .fillMaxWidth() + .background(Color(0xFF388DF2)) + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally + ) { + Card( + modifier = Modifier.size(90.dp), + shape = RoundedCornerShape(45.dp), + ) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Text( + text = currentUser().username[0].toString(), + fontSize = 30.sp, + ) + } + } + Spacer(modifier = Modifier.fillMaxHeight(0.1f)) + Text( + text = currentUser().username, + fontSize = 30.sp, + color = Color.White + ) + } + } + Divider() + Row( + modifier = Modifier + .fillMaxHeight(0.9f) + .fillMaxWidth() + .background(Color.White), + ) { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Bottom, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Box( + modifier = Modifier.fillMaxHeight(0.2f), + contentAlignment = Alignment.Center, + ) { + + Text( + modifier = Modifier.clickable { + navController.navigate(ScreenPaths.Login.route) + }, + text = "Выйти", + fontSize = 20.sp, + color = Color.Red + ) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/screens/RegisterScreen.kt b/app/src/main/java/com/example/dtf/screens/RegisterScreen.kt new file mode 100644 index 0000000..7fcad36 --- /dev/null +++ b/app/src/main/java/com/example/dtf/screens/RegisterScreen.kt @@ -0,0 +1,109 @@ +package com.example.dtf.screens + +import android.graphics.drawable.shapes.Shape +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.* +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.navigation.NavController +import androidx.navigation.NavHostController +import com.example.dtf.utils.ScreenPaths +import com.example.dtf.widgets.MyTextField + +@Composable +fun RegisterScreen(navController: NavHostController) { + val login = remember { mutableStateOf(TextFieldValue(""))} + val password = remember { mutableStateOf(TextFieldValue(""))} + val confirmPassword = remember { mutableStateOf(TextFieldValue(""))} + Row( + horizontalArrangement = Arrangement.Center, + modifier = Modifier.fillMaxSize() + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxWidth(0.7f).fillMaxHeight().padding(vertical=100.dp) + ) { + Text( + text = "Регистрация", + style = TextStyle( + fontSize = 40.sp, + fontWeight = FontWeight(400), + color = Color(0xFF000000), + ), + modifier = Modifier.fillMaxHeight(0.2f) + ) + MyTextField( + login, + "Логин", + {login.value = it}, + modifier = Modifier + .padding(bottom=30.dp) + .fillMaxWidth() + .heightIn(max=55.dp) + ) + MyTextField( + password, + "Пароль", + {password.value = it}, + modifier = Modifier + .padding(bottom=30.dp) + .fillMaxWidth() + .heightIn(max=55.dp) + ) + MyTextField( + confirmPassword, + "Пароль повторно", + {confirmPassword.value = it}, + modifier = Modifier + .padding(bottom=60.dp) + .fillMaxWidth() + .heightIn(max=55.dp) + ) + Button( + {}, + colors = ButtonDefaults.buttonColors(Color(0xFF388DF2)), + border = BorderStroke(1.dp, Color(0xFF0085FF)), + shape = RoundedCornerShape(15.dp), + modifier = Modifier.padding(bottom=20.dp).fillMaxWidth() + ) { + Text( + text = "Зарегистрироваться", + style = TextStyle( + fontSize = 24.sp, + fontWeight = FontWeight(400), + color = Color(0xFFFFFFFF), + ) + ) + } + Text( + text = "Уже есть аккаунт?", + style = TextStyle( + fontSize = 16.sp, + fontWeight = FontWeight(400), + color = Color(0xFFB6B3B3), + ), + modifier = Modifier.clickable { + navController.navigate(ScreenPaths.Login.route) + } + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/ui/theme/Color.kt b/app/src/main/java/com/example/dtf/ui/theme/Color.kt new file mode 100644 index 0000000..6cd0c82 --- /dev/null +++ b/app/src/main/java/com/example/dtf/ui/theme/Color.kt @@ -0,0 +1,11 @@ +package com.example.dtf.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/ui/theme/Shape.kt b/app/src/main/java/com/example/dtf/ui/theme/Shape.kt new file mode 100644 index 0000000..2794e6d --- /dev/null +++ b/app/src/main/java/com/example/dtf/ui/theme/Shape.kt @@ -0,0 +1,11 @@ +package com.example.dtf.ui.theme + +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Shapes +import androidx.compose.ui.unit.dp + +val Shapes = Shapes( + small = RoundedCornerShape(4.dp), + medium = RoundedCornerShape(4.dp), + large = RoundedCornerShape(0.dp) +) \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/ui/theme/Theme.kt b/app/src/main/java/com/example/dtf/ui/theme/Theme.kt new file mode 100644 index 0000000..f4aa5bc --- /dev/null +++ b/app/src/main/java/com/example/dtf/ui/theme/Theme.kt @@ -0,0 +1,41 @@ +package com.example.dtf.ui.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material.MaterialTheme +import androidx.compose.material.darkColors +import androidx.compose.material.lightColors +import androidx.compose.runtime.Composable + +private val DarkColorPalette = darkColors( + +) + +private val LightColorPalette = lightColors( + /* Other default colors to override + background = Color.White, + surface = Color.White, + onPrimary = Color.White, + onSecondary = Color.Black, + onBackground = Color.Black, + onSurface = Color.Black, + */ +) + +@Composable +fun DTFTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + content: @Composable () -> Unit +) { + val colors = if (darkTheme) { + DarkColorPalette + } else { + LightColorPalette + } + + MaterialTheme( + colors = colors, + typography = Typography, + shapes = Shapes, + content = content + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/ui/theme/Type.kt b/app/src/main/java/com/example/dtf/ui/theme/Type.kt new file mode 100644 index 0000000..3893e51 --- /dev/null +++ b/app/src/main/java/com/example/dtf/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package com.example.dtf.ui.theme + +import androidx.compose.material.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( + body1 = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/utils/ScreenPaths.kt b/app/src/main/java/com/example/dtf/utils/ScreenPaths.kt new file mode 100644 index 0000000..ea4c889 --- /dev/null +++ b/app/src/main/java/com/example/dtf/utils/ScreenPaths.kt @@ -0,0 +1,12 @@ +package com.example.dtf.utils + +sealed class ScreenPaths (val route: String){ + object Auth: ScreenPaths("auth") + object Login: ScreenPaths("auth/login") + object Register: ScreenPaths("auth/register") + object Post: ScreenPaths("posts/{post}") + object Posts: ScreenPaths("posts") + object NewPost: ScreenPaths("posts/new") + object EditPost: ScreenPaths("posts/{post}/edit") + object Profile: ScreenPaths("profile") +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/widgets/BottomNavBar.kt b/app/src/main/java/com/example/dtf/widgets/BottomNavBar.kt new file mode 100644 index 0000000..7002357 --- /dev/null +++ b/app/src/main/java/com/example/dtf/widgets/BottomNavBar.kt @@ -0,0 +1,72 @@ +package com.example.dtf.widgets + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.material.BottomNavigation +import androidx.compose.material.BottomNavigationItem +import androidx.compose.material.Icon +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material.icons.filled.Home +import androidx.compose.material.icons.filled.Person +import androidx.compose.runtime.Composable +import androidx.navigation.NavController +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.navigation.compose.currentBackStackEntryAsState +import com.example.dtf.models.currentUser +import com.example.dtf.utils.ScreenPaths + +@Composable +fun BottomNavBar(navController: NavController) { + BottomNavigation( + modifier = Modifier.height(70.dp), + backgroundColor = Color.White, + contentColor = Color.Black + ) { + val navBackStackEntry by navController.currentBackStackEntryAsState() + val currentRoute = navBackStackEntry?.destination?.route + listOfNotNull( + Triple(ScreenPaths.Posts.route, Icons.Default.Home, "Новости"), + if (currentUser().isModerator) { + Triple(ScreenPaths.NewPost.route, Icons.Default.Edit, "Создать") + } else { null }, + Triple(ScreenPaths.Profile.route, Icons.Default.Person, "Профиль") + ).forEach { x -> + BottomNavigationItem( + icon = { + Icon( + imageVector = x.second, + contentDescription = x.third, + modifier = Modifier.size(42.dp) + ) }, + selectedContentColor = Color.Black, + unselectedContentColor = Color.Black.copy(0.4f), + alwaysShowLabel = true, + selected = currentRoute == x.first, + onClick = { + navController.navigate(x.first) { + navController.graph.startDestinationRoute?.let { route -> + popUpTo(route) { + saveState = true + } + } + launchSingleTop = true + restoreState = true + } + }, + label = { Text(x.third) } + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dtf/widgets/MyTextField.kt b/app/src/main/java/com/example/dtf/widgets/MyTextField.kt new file mode 100644 index 0000000..1889b06 --- /dev/null +++ b/app/src/main/java/com/example/dtf/widgets/MyTextField.kt @@ -0,0 +1,45 @@ +package com.example.dtf.widgets + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Text +import androidx.compose.material.TextField +import androidx.compose.material.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +@Composable +fun MyTextField(value: MutableState, placeholder: String, onChanged: (TextFieldValue) -> Unit, modifier: Modifier, backgroundColor: Color = Color(0xFFF2F2F2), singleLine: Boolean = true) { + TextField( + value.value, onChanged, + shape = RoundedCornerShape(size = 15.dp), + colors = TextFieldDefaults.textFieldColors( + backgroundColor = backgroundColor, + disabledTextColor = Color.Transparent, + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent, + disabledIndicatorColor = Color.Transparent, + ), + placeholder = { + Text( + text=placeholder, + fontSize = 16.sp, + color = Color(0xFFAAAAAA) + ) + }, + singleLine = singleLine, + textStyle = TextStyle( + fontSize = 16.sp + ), + modifier = modifier + .background(color = backgroundColor, shape = RoundedCornerShape(size = 15.dp)) + .border(width = 1.dp, color = Color(0xFFC7C7C7), shape = RoundedCornerShape(size = 15.dp)) + ) +} \ No newline at end of file diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/font/jejugothic_regular.ttf b/app/src/main/res/font/jejugothic_regular.ttf new file mode 100644 index 0000000..a6886c3 Binary files /dev/null and b/app/src/main/res/font/jejugothic_regular.ttf differ diff --git a/app/src/main/res/font/marckscript_regular.ttf b/app/src/main/res/font/marckscript_regular.ttf new file mode 100644 index 0000000..024294b Binary files /dev/null and b/app/src/main/res/font/marckscript_regular.ttf differ diff --git a/app/src/main/res/font/nunito_wght.ttf b/app/src/main/res/font/nunito_wght.ttf new file mode 100644 index 0000000..0a00f63 Binary files /dev/null and b/app/src/main/res/font/nunito_wght.ttf differ 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..eca70cf --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ 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..eca70cf --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v33/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v33/ic_launcher.xml new file mode 100644 index 0000000..6f3b755 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v33/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000..c209e78 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000..b2dfe3d Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000..4f0f1d6 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000..62b611d Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000..948a307 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..1b9a695 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000..28d4b77 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9287f50 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000..aa7d642 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000..9126ae3 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..ef8ebd2 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + DTF + \ 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..ee99725 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..fa0f996 --- /dev/null +++ b/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/test/java/com/example/dtf/ExampleUnitTest.kt b/app/src/test/java/com/example/dtf/ExampleUnitTest.kt new file mode 100644 index 0000000..5782e8d --- /dev/null +++ b/app/src/test/java/com/example/dtf/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.example.dtf + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..44023d2 --- /dev/null +++ b/build.gradle @@ -0,0 +1,16 @@ +buildscript { + ext { + compose_ui_version = '1.2.0' + } + repositories { + google() + } + dependencies { + classpath 'com.android.tools.build:gradle:4.0.0' + } +}// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + id 'com.android.application' version '7.4.2' apply false + id 'com.android.library' version '7.4.2' apply false + id 'org.jetbrains.kotlin.android' version '1.7.0' apply false +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..239f54f --- /dev/null +++ b/gradle.properties @@ -0,0 +1,26 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true +android.defaults.buildfeatures.buildconfig=true +android.nonFinalResIds=false +android.suppressUnsupportedCompileSdk=34 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..9437bda --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Oct 06 03:21:29 GMT+04:00 2023 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/local.properties b/local.properties new file mode 100644 index 0000000..f7299d2 --- /dev/null +++ b/local.properties @@ -0,0 +1,8 @@ +## This file must *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. +# +# Location of the SDK. This is only used by Gradle. +# For customization when using a Version Control System, please read the +# header note. +#Tue Nov 07 09:36:13 SAMT 2023 +sdk.dir=C\:\\Users\\Aqua\\AppData\\Local\\Android\\Sdk diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..3d74695 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,16 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} +rootProject.name = "DTF" +include ':app'