Compare commits

...

No commits in common. "main" and "master" have entirely different histories.
main ... master

55 changed files with 1758 additions and 37 deletions

35
.gitignore vendored
View File

@ -1,35 +0,0 @@
# ---> Android
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Log/OS Files
*.log
# Android Studio generated files and folders
captures/
.externalNativeBuild/
.cxx/
*.apk
output.json
# IntelliJ
*.iml
.idea/
misc.xml
deploymentTargetDropDown.xml
render.experimental.xml
# Keystore files
*.jks
*.keystore
# Google Services (e.g. APIs or Firebase)
google-services.json
# Android Profiling
*.hprof

View File

@ -1,2 +0,0 @@
# Polevoy_PMD

1
app/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

65
app/build.gradle Normal file
View File

@ -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"
}

21
app/proguard-rules.pro vendored Normal file
View File

@ -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

View File

@ -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)
}
}

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.DTF"
tools:targetApi="31">
<activity
android:name="com.example.dtf.MainActivity"
android:windowSoftInputMode="adjustResize"
android:exported="true"
android:theme="@style/Theme.DTF">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -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()
)
}
}
}
}
}
}
}
}

View File

@ -0,0 +1,14 @@
package com.example.dtf.models
data class Category(
val id: Int,
val name: String
)
fun getAllCategories() : List<Category> {
return listOf(
Category(1, "Игры"),
Category(2, "Кино"),
Category(3, "Аниме"),
)
}

View File

@ -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
)

View File

@ -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<Comment>,
val date: Date,
)
fun getAllPosts() : List<Post> {
return posts
}
fun getPostsByCategory(categoryId: Int) : List<Post> {
return posts.filter { it.categoryId == categoryId }
}

View File

@ -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<User> {
return listOf(
User(1, "Sheriff", "123456", true),
User(2, "Shailushaika", "654321", false),
)
}
fun currentUser() : User {
return getAllUsers()[0]
}

View File

@ -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))
}
}

View File

@ -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
}
}
}
)
}
}
}

View File

@ -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))
}
}

View File

@ -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
)
}
}

View File

@ -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<String>) {
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
)
}
}
}
}
}

View File

@ -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
)
}
}
}
}
}

View File

@ -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)
}
)
}
}
}

View File

@ -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)

View File

@ -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)
)

View File

@ -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
)
}

View File

@ -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
)
*/
)

View File

@ -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")
}

View File

@ -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) }
)
}
}
}

View File

@ -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<TextFieldValue>, 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))
)
}

View File

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>

View File

@ -0,0 +1,3 @@
<resources>
<string name="app_name">DTF</string>
</resources>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.DTF" parent="android:Theme.Material.Light.NoActionBar">
<item name="android:statusBarColor">@color/purple_700</item>
</style>
</resources>

View File

@ -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>

View File

@ -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>

View File

@ -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)
}
}

16
build.gradle Normal file
View File

@ -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
}

26
gradle.properties Normal file
View File

@ -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

View File

@ -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

8
local.properties Normal file
View File

@ -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

16
settings.gradle Normal file
View File

@ -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'