@ -0,0 +1,76 @@
@@ -0,0 +1,76 @@
|
||||
|
||||
plugins { |
||||
id("com.android.application") |
||||
id("org.jetbrains.kotlin.android") |
||||
id("com.google.devtools.ksp") version "2.1.0-1.0.29" |
||||
id("org.jetbrains.kotlin.plugin.compose") |
||||
} |
||||
|
||||
android { |
||||
compileSdk = 35 |
||||
|
||||
defaultConfig { |
||||
applicationId = "ru.asakul.feel" |
||||
minSdk = 26 |
||||
targetSdk = 35 |
||||
versionCode = 1 |
||||
versionName = "1.0" |
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" |
||||
vectorDrawables { |
||||
useSupportLibrary = true |
||||
} |
||||
} |
||||
|
||||
buildTypes { |
||||
release { |
||||
isMinifyEnabled = false |
||||
proguardFiles( |
||||
getDefaultProguardFile("proguard-android-optimize.txt"), |
||||
"proguard-rules.pro" |
||||
) |
||||
} |
||||
} |
||||
compileOptions { |
||||
sourceCompatibility = JavaVersion.VERSION_17 |
||||
targetCompatibility = JavaVersion.VERSION_17 |
||||
} |
||||
kotlinOptions { |
||||
jvmTarget = "17" |
||||
} |
||||
buildFeatures { |
||||
compose = true |
||||
} |
||||
packaging { |
||||
resources { |
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}" |
||||
} |
||||
} |
||||
namespace = "ru.asakul.feel" |
||||
} |
||||
|
||||
dependencies { |
||||
// Import the Compose BOM |
||||
implementation(platform("androidx.compose:compose-bom:2024.12.01")) |
||||
implementation("androidx.activity:activity-compose:1.9.3") |
||||
implementation("androidx.compose.material3:material3") |
||||
implementation("androidx.compose.ui:ui") |
||||
implementation("androidx.compose.ui:ui-tooling") |
||||
implementation("androidx.compose.ui:ui-tooling-preview") |
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7") |
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7") |
||||
implementation("androidx.navigation:navigation-compose:2.8.5") |
||||
|
||||
|
||||
// Testing |
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1") |
||||
androidTestImplementation("androidx.test.ext:junit:1.2.1") |
||||
|
||||
//Room |
||||
implementation("androidx.room:room-runtime:${rootProject.extra["room_version"]}") |
||||
ksp("androidx.room:room-compiler:${rootProject.extra["room_version"]}") |
||||
implementation("androidx.room:room-ktx:${rootProject.extra["room_version"]}") |
||||
testImplementation(kotlin("test")) |
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,21 @@
@@ -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 |
||||
@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
|
||||
package ru.asakul.feel |
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry |
||||
import androidx.test.ext.junit.runners.AndroidJUnit4 |
||||
|
||||
import org.junit.Test |
||||
import org.junit.runner.RunWith |
||||
|
||||
import org.junit.Assert.* |
||||
|
||||
/** |
||||
* Instrumented test, which will execute on an Android device. |
||||
* |
||||
* See [testing documentation](http://d.android.com/tools/testing). |
||||
*/ |
||||
@RunWith(AndroidJUnit4::class) |
||||
class ExampleInstrumentedTest { |
||||
@Test |
||||
fun useAppContext() { |
||||
// Context of the app under test. |
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext |
||||
assertEquals("ru.asakul.feel", appContext.packageName) |
||||
} |
||||
} |
||||
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
<?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:name=".FeelApplication" |
||||
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:roundIcon="@mipmap/ic_launcher_round" |
||||
android:supportsRtl="true" |
||||
android:theme="@style/Theme.Feel"> |
||||
<activity |
||||
android:name=".MainActivity" |
||||
android:exported="true" |
||||
android:label="@string/app_name" |
||||
android:theme="@style/Theme.Feel"> |
||||
<intent-filter> |
||||
<action android:name="android.intent.action.MAIN" /> |
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" /> |
||||
</intent-filter> |
||||
</activity> |
||||
</application> |
||||
|
||||
</manifest> |
||||
@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
|
||||
package ru.asakul.feel |
||||
|
||||
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory |
||||
import androidx.lifecycle.viewmodel.CreationExtras |
||||
import androidx.lifecycle.viewmodel.initializer |
||||
import androidx.lifecycle.viewmodel.viewModelFactory |
||||
import ru.asakul.feel.ui.mood.MoodEntryViewModel |
||||
import ru.asakul.feel.ui.moodlist.MoodListScreenViewModel |
||||
|
||||
object AppViewModelProvider { |
||||
val Factory = viewModelFactory { |
||||
initializer { |
||||
MoodEntryViewModel(moodEntryRepository = feelApplication().container.moodEntryRepository) |
||||
} |
||||
|
||||
initializer { |
||||
MoodListScreenViewModel(moodEntryRepository = feelApplication().container.moodEntryRepository) |
||||
} |
||||
} |
||||
} |
||||
|
||||
fun CreationExtras.feelApplication(): FeelApplication = |
||||
(this[AndroidViewModelFactory.APPLICATION_KEY] as FeelApplication) |
||||
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
package ru.asakul.feel |
||||
|
||||
import android.app.Application |
||||
import ru.asakul.feel.data.AppContainer |
||||
import ru.asakul.feel.data.AppDataContainer |
||||
|
||||
class FeelApplication : Application() { |
||||
|
||||
lateinit var container: AppContainer |
||||
|
||||
override fun onCreate() { |
||||
super.onCreate() |
||||
container = AppDataContainer(this) |
||||
} |
||||
} |
||||
@ -0,0 +1,66 @@
@@ -0,0 +1,66 @@
|
||||
package ru.asakul.feel |
||||
|
||||
import android.os.Bundle |
||||
import androidx.activity.ComponentActivity |
||||
import androidx.activity.compose.setContent |
||||
import androidx.activity.enableEdgeToEdge |
||||
import androidx.compose.foundation.layout.Row |
||||
import androidx.compose.foundation.layout.fillMaxSize |
||||
import androidx.compose.foundation.layout.padding |
||||
import androidx.compose.material3.Scaffold |
||||
import androidx.compose.material3.Surface |
||||
import androidx.compose.material3.Text |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.runtime.getValue |
||||
import androidx.compose.runtime.mutableStateOf |
||||
import androidx.compose.runtime.saveable.rememberSaveable |
||||
import androidx.compose.runtime.setValue |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.res.stringResource |
||||
import androidx.compose.ui.tooling.preview.Preview |
||||
import ru.asakul.feel.ui.mood.MoodEntryViewModel |
||||
import ru.asakul.feel.ui.mood.NewMoodEntry |
||||
import ru.asakul.feel.ui.theme.AppTheme |
||||
import androidx.lifecycle.viewmodel.compose.viewModel |
||||
import androidx.navigation.compose.rememberNavController |
||||
import ru.asakul.feel.navigation.FeelNavHost |
||||
import ru.asakul.feel.navigation.MainScreen |
||||
import ru.asakul.feel.ui.moodlist.MoodListScreen |
||||
|
||||
class MainActivity : ComponentActivity() { |
||||
override fun onCreate(savedInstanceState: Bundle?) { |
||||
super.onCreate(savedInstanceState) |
||||
enableEdgeToEdge() |
||||
setContent { |
||||
AppTheme { |
||||
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> |
||||
MyApp( |
||||
modifier = Modifier.padding(innerPadding) |
||||
) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Composable |
||||
fun MyApp( |
||||
modifier : Modifier = Modifier) { |
||||
var navController = rememberNavController() |
||||
Surface ( |
||||
modifier = modifier |
||||
) { |
||||
FeelNavHost( |
||||
navController, |
||||
modifier |
||||
) |
||||
} |
||||
} |
||||
|
||||
@Preview(showBackground = true, widthDp = 400) |
||||
@Composable |
||||
fun GreetingPreview() { |
||||
AppTheme { |
||||
MyApp() |
||||
} |
||||
} |
||||
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
package ru.asakul.feel.data |
||||
|
||||
import android.content.Context |
||||
|
||||
interface AppContainer { |
||||
val moodEntryRepository: MoodEntryRepository |
||||
} |
||||
|
||||
class AppDataContainer(private val context: Context) : AppContainer { |
||||
override val moodEntryRepository: MoodEntryRepository by lazy { |
||||
OfflineMoodEntryRepository(MoodEntryDatabase.getDatabase(context).itemDao()) |
||||
} |
||||
} |
||||
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
package ru.asakul.feel.data |
||||
|
||||
import androidx.room.Entity |
||||
import androidx.room.PrimaryKey |
||||
import java.sql.Timestamp |
||||
|
||||
@Entity(tableName = "mood_entries") |
||||
data class MoodEntry( |
||||
@PrimaryKey(autoGenerate = true) |
||||
val id: Long, |
||||
val timestamp: Long, |
||||
val moodLevel: Int, |
||||
val emotions: String, |
||||
val reasons: String |
||||
) |
||||
@ -0,0 +1,27 @@
@@ -0,0 +1,27 @@
|
||||
package ru.asakul.feel.data |
||||
|
||||
import androidx.room.Dao |
||||
import androidx.room.Delete |
||||
import androidx.room.Insert |
||||
import androidx.room.OnConflictStrategy |
||||
import androidx.room.Query |
||||
import androidx.room.Update |
||||
import kotlinx.coroutines.flow.Flow |
||||
|
||||
@Dao |
||||
interface MoodEntryDao { |
||||
@Insert(onConflict = OnConflictStrategy.IGNORE) |
||||
suspend fun insert(moodEntry: MoodEntry) |
||||
|
||||
@Update |
||||
suspend fun update(moodEntry: MoodEntry) |
||||
|
||||
@Delete |
||||
suspend fun delete(moodEntry: MoodEntry) |
||||
|
||||
@Query("SELECT * FROM mood_entries WHERE id = :id") |
||||
fun getItem(id: Long): Flow<MoodEntry> |
||||
|
||||
@Query("SELECT * FROM mood_entries ORDER BY timestamp DESC") |
||||
fun getAllItems(): Flow<List<MoodEntry>> |
||||
} |
||||
@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
|
||||
package ru.asakul.feel.data |
||||
|
||||
import android.content.Context |
||||
import androidx.room.Database |
||||
import androidx.room.Room |
||||
import androidx.room.RoomDatabase |
||||
|
||||
@Database(entities = [MoodEntry::class], version = 1, exportSchema = false) |
||||
abstract class MoodEntryDatabase : RoomDatabase() { |
||||
abstract fun itemDao(): MoodEntryDao |
||||
|
||||
companion object { |
||||
@Volatile |
||||
private var Instance: MoodEntryDatabase? = null |
||||
|
||||
fun getDatabase(context: Context): MoodEntryDatabase { |
||||
return Instance ?: synchronized(this) { |
||||
Room.databaseBuilder(context, MoodEntryDatabase::class.java, "mood_entry_database") |
||||
.fallbackToDestructiveMigration().build().also { Instance = it } |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,15 @@
@@ -0,0 +1,15 @@
|
||||
package ru.asakul.feel.data |
||||
|
||||
import kotlinx.coroutines.flow.Flow |
||||
|
||||
interface MoodEntryRepository { |
||||
fun getAllMoodEntriesStream(): Flow<List<MoodEntry>> |
||||
|
||||
fun getMoodEntryStream(id: Long): Flow<MoodEntry?> |
||||
|
||||
suspend fun insertMoodEntry(item: MoodEntry) |
||||
|
||||
suspend fun deleteMoodEntry(item: MoodEntry) |
||||
|
||||
suspend fun updateMoodEntry(item: MoodEntry) |
||||
} |
||||
@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
|
||||
package ru.asakul.feel.data |
||||
|
||||
import kotlinx.coroutines.flow.Flow |
||||
|
||||
class OfflineMoodEntryRepository(private val moodEntryDao: MoodEntryDao) : MoodEntryRepository { |
||||
|
||||
override fun getAllMoodEntriesStream(): Flow<List<MoodEntry>> = moodEntryDao.getAllItems() |
||||
|
||||
override fun getMoodEntryStream(id: Long): Flow<MoodEntry?> = moodEntryDao.getItem(id) |
||||
|
||||
override suspend fun insertMoodEntry(item: MoodEntry) = moodEntryDao.insert(item) |
||||
|
||||
override suspend fun deleteMoodEntry(item: MoodEntry) = moodEntryDao.delete(item) |
||||
|
||||
override suspend fun updateMoodEntry(item: MoodEntry) = moodEntryDao.update(item) |
||||
} |
||||
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
package ru.asakul.feel.domain |
||||
|
||||
import android.content.Context |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.platform.LocalContext |
||||
import androidx.compose.ui.res.stringResource |
||||
import ru.asakul.feel.R |
||||
|
||||
class Emotions() { |
||||
@Composable |
||||
fun allEmotions() = setOf( |
||||
stringResource(R.string.happiness), |
||||
stringResource(R.string.elation), |
||||
stringResource(R.string.calmness), |
||||
stringResource(R.string.tranquility), |
||||
stringResource(R.string.gratitude), |
||||
stringResource(R.string.surprise), |
||||
stringResource(R.string.fatigue), |
||||
stringResource(R.string.exhaustion), |
||||
stringResource(R.string.anger), |
||||
stringResource(R.string.sadness), |
||||
stringResource(R.string.fear), |
||||
stringResource(R.string.panic), |
||||
stringResource(R.string.irritation), |
||||
stringResource(R.string.apathy), |
||||
stringResource(R.string.hope), |
||||
stringResource(R.string.relief), |
||||
stringResource(R.string.confidence), |
||||
stringResource(R.string.satisfaction), |
||||
stringResource(R.string.joy), |
||||
stringResource(R.string.enthusiasm), |
||||
stringResource(R.string.pride), |
||||
stringResource(R.string.anxiety), |
||||
stringResource(R.string.nervousness), |
||||
stringResource(R.string.boldness), |
||||
stringResource(R.string.disgust), |
||||
stringResource(R.string.jealousy), |
||||
stringResource(R.string.guilt), |
||||
stringResource(R.string.embarrassment), |
||||
stringResource(R.string.disenchantment), |
||||
stringResource(R.string.stress), |
||||
stringResource(R.string.loneliness) |
||||
) |
||||
} |
||||
@ -0,0 +1,9 @@
@@ -0,0 +1,9 @@
|
||||
package ru.asakul.feel.domain |
||||
|
||||
enum class MoodLevel { |
||||
VERY_BAD, |
||||
BAD, |
||||
NORMAL, |
||||
GOOD, |
||||
AWESOME |
||||
} |
||||
@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
|
||||
package ru.asakul.feel.domain |
||||
|
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.res.stringResource |
||||
import ru.asakul.feel.R |
||||
|
||||
class Reasons { |
||||
|
||||
@Composable |
||||
fun allReasons() = setOf ( |
||||
stringResource(R.string.home), |
||||
stringResource(R.string.work), |
||||
stringResource(R.string.hobby), |
||||
stringResource(R.string.family), |
||||
stringResource(R.string.society), |
||||
stringResource(R.string.exercise), |
||||
stringResource(R.string.politics), |
||||
stringResource(R.string.weather), |
||||
stringResource(R.string.health), |
||||
stringResource(R.string.sleep), |
||||
stringResource(R.string.food), |
||||
stringResource(R.string.money) |
||||
) |
||||
} |
||||
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
package ru.asakul.feel.navigation |
||||
|
||||
interface FeelDestination { |
||||
val route: String |
||||
} |
||||
|
||||
object MainScreen : FeelDestination { |
||||
override val route = "main" |
||||
} |
||||
|
||||
object NewEntryScreen : FeelDestination { |
||||
override val route = "new_entry" |
||||
} |
||||
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
package ru.asakul.feel.navigation |
||||
|
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.navigation.NavHostController |
||||
import androidx.navigation.compose.NavHost |
||||
import androidx.navigation.compose.composable |
||||
import ru.asakul.feel.ui.mood.NewMoodEntry |
||||
import ru.asakul.feel.ui.moodlist.MoodListScreen |
||||
|
||||
|
||||
@Composable |
||||
fun FeelNavHost ( |
||||
navController: NavHostController, |
||||
modifier: Modifier = Modifier |
||||
) { |
||||
NavHost( |
||||
navController = navController, |
||||
startDestination = MainScreen.route, |
||||
modifier = modifier) { |
||||
composable (route = MainScreen.route) { |
||||
MoodListScreen( |
||||
onNewMoodEntryClick = { |
||||
navController.navigate(NewEntryScreen.route) |
||||
} |
||||
) |
||||
} |
||||
composable (route = NewEntryScreen.route) { |
||||
NewMoodEntry(onDoneClick = { |
||||
navController.navigate(MainScreen.route) |
||||
}) |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
package ru.asakul.feel.ui |
||||
|
||||
import ru.asakul.feel.data.MoodEntry |
||||
import java.sql.Timestamp |
||||
|
||||
data class MoodEntryRepr( |
||||
val id : Long = 0, |
||||
val timestamp: Timestamp = Timestamp(0), |
||||
val moodLevel: Int = 0, |
||||
val emotions: List<String> = listOf(), |
||||
val reasons: List<String> = listOf(), |
||||
) |
||||
|
||||
fun MoodEntryRepr.toMoodEntry(): MoodEntry = MoodEntry( |
||||
id = id, |
||||
timestamp = timestamp.time, |
||||
moodLevel = moodLevel, |
||||
emotions = emotions.joinToString (","), |
||||
reasons = reasons.joinToString (",") |
||||
) |
||||
|
||||
fun MoodEntry.toMoodEntryRepr(): MoodEntryRepr = MoodEntryRepr( |
||||
id = id, |
||||
timestamp = Timestamp(timestamp), |
||||
moodLevel = moodLevel, |
||||
emotions = emotions.split(","), |
||||
reasons = reasons.split(",") |
||||
) |
||||
@ -0,0 +1,137 @@
@@ -0,0 +1,137 @@
|
||||
package ru.asakul.feel.ui.mood |
||||
|
||||
import androidx.compose.foundation.background |
||||
import androidx.compose.foundation.layout.Arrangement |
||||
import androidx.compose.foundation.layout.Column |
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi |
||||
import androidx.compose.foundation.layout.FlowRow |
||||
import androidx.compose.foundation.layout.Row |
||||
import androidx.compose.foundation.layout.fillMaxWidth |
||||
import androidx.compose.foundation.layout.padding |
||||
import androidx.compose.foundation.rememberScrollState |
||||
import androidx.compose.foundation.verticalScroll |
||||
import androidx.compose.material3.Button |
||||
import androidx.compose.material3.InputChip |
||||
import androidx.compose.material3.MaterialTheme |
||||
import androidx.compose.material3.OutlinedButton |
||||
import androidx.compose.material3.Surface |
||||
import androidx.compose.material3.Text |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.runtime.getValue |
||||
import androidx.compose.runtime.mutableIntStateOf |
||||
import androidx.compose.runtime.mutableStateOf |
||||
import androidx.compose.runtime.rememberCoroutineScope |
||||
import androidx.compose.runtime.saveable.rememberSaveable |
||||
import androidx.compose.runtime.setValue |
||||
import androidx.compose.ui.Alignment |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.res.stringResource |
||||
import androidx.compose.ui.unit.dp |
||||
import androidx.lifecycle.viewmodel.compose.viewModel |
||||
import kotlinx.coroutines.launch |
||||
import ru.asakul.feel.AppViewModelProvider |
||||
import ru.asakul.feel.R |
||||
import ru.asakul.feel.domain.Emotions |
||||
import ru.asakul.feel.domain.Reasons |
||||
import ru.asakul.feel.ui.mood.MoodEntryViewModel |
||||
import kotlin.collections.minus |
||||
import kotlin.collections.plus |
||||
|
||||
@Composable |
||||
fun MoodLevelSelector( |
||||
modifier : Modifier = Modifier, |
||||
onMoodLevelSelected : (Int) -> Unit) { |
||||
var selectedLevel by rememberSaveable { mutableIntStateOf(0) } |
||||
Surface(modifier = modifier.padding(4.dp).background(MaterialTheme.colorScheme.primary)) { |
||||
Row(modifier = Modifier.padding(4.dp)) { |
||||
for (level in 1..5) { |
||||
if (level != selectedLevel) { |
||||
OutlinedButton( |
||||
{ |
||||
onMoodLevelSelected(level) |
||||
selectedLevel = level |
||||
}, |
||||
modifier = Modifier.padding(4.dp), |
||||
) { Text("$level") } |
||||
} else { |
||||
Button( |
||||
onClick = { |
||||
onMoodLevelSelected(level) |
||||
selectedLevel = level |
||||
}, |
||||
modifier = Modifier.padding(4.dp), |
||||
) { |
||||
Text("$level") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@OptIn(ExperimentalLayoutApi::class) |
||||
@Composable |
||||
fun TagSelector( |
||||
tags : Set<String>, |
||||
modifier : Modifier = Modifier, |
||||
onSelectedChange : (Set<String>) -> Unit |
||||
) { |
||||
var selectedTags by rememberSaveable { mutableStateOf(setOf<String>()) } |
||||
FlowRow (modifier = modifier) { |
||||
for (tag in tags) { |
||||
val isSelected = tag in selectedTags |
||||
InputChip( |
||||
onClick = { |
||||
if (isSelected) |
||||
selectedTags -= tag else |
||||
selectedTags += tag |
||||
onSelectedChange(selectedTags) |
||||
}, |
||||
label = { Text(tag) }, |
||||
selected = isSelected, |
||||
modifier = Modifier.padding(2.dp) |
||||
) |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Composable |
||||
fun NewMoodEntry( |
||||
onDoneClick: () -> Unit, |
||||
modifier : Modifier = Modifier, |
||||
viewModel: MoodEntryViewModel = viewModel(factory = AppViewModelProvider.Factory)) { |
||||
|
||||
val coroutineScope = rememberCoroutineScope () |
||||
|
||||
Row (modifier.fillMaxWidth().padding(4.dp), |
||||
horizontalArrangement = Arrangement.Center){ |
||||
Column ( |
||||
horizontalAlignment = Alignment.CenterHorizontally, |
||||
modifier = Modifier.verticalScroll(rememberScrollState())){ |
||||
Text (stringResource(R.string.how_s_your_mood_right_now)) |
||||
MoodLevelSelector(onMoodLevelSelected = { viewModel.updateUiState(viewModel.moodEntryState.moodEntry.copy( |
||||
moodLevel = it |
||||
))}) |
||||
Text (stringResource(R.string.what_do_you_feel)) |
||||
TagSelector(Emotions().allEmotions(), onSelectedChange = { |
||||
viewModel.updateUiState(viewModel.moodEntryState.moodEntry.copy( |
||||
emotions = it.toList() |
||||
)) |
||||
}) |
||||
Text(stringResource(R.string.why_do_you_feel_that_way)) |
||||
TagSelector(Reasons().allReasons(), onSelectedChange = { |
||||
viewModel.updateUiState(viewModel.moodEntryState.moodEntry.copy( |
||||
reasons = it.toList() |
||||
)) |
||||
}) |
||||
Button(onClick = { |
||||
coroutineScope.launch { |
||||
viewModel.saveMoodEntry() |
||||
onDoneClick() |
||||
} |
||||
}) { |
||||
Text(stringResource(R.string.done)) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,30 @@
@@ -0,0 +1,30 @@
|
||||
package ru.asakul.feel.ui.mood |
||||
|
||||
import androidx.compose.runtime.getValue |
||||
import androidx.compose.runtime.mutableStateOf |
||||
import androidx.compose.runtime.setValue |
||||
import androidx.lifecycle.ViewModel |
||||
import ru.asakul.feel.data.MoodEntry |
||||
import ru.asakul.feel.data.MoodEntryRepository |
||||
import ru.asakul.feel.ui.MoodEntryRepr |
||||
import ru.asakul.feel.ui.toMoodEntry |
||||
import java.sql.Timestamp |
||||
import java.time.Instant |
||||
|
||||
class MoodEntryViewModel(val moodEntryRepository: MoodEntryRepository) : ViewModel() { |
||||
var moodEntryState by mutableStateOf(MoodEntryUiState(MoodEntryRepr())) |
||||
private set |
||||
|
||||
fun updateUiState(moodEntryRepr: MoodEntryRepr) { |
||||
moodEntryState = MoodEntryUiState(moodEntry = moodEntryRepr) |
||||
} |
||||
|
||||
suspend fun saveMoodEntry() { |
||||
moodEntryRepository.insertMoodEntry(moodEntryState.moodEntry |
||||
.toMoodEntry().copy(timestamp = Instant.now().toEpochMilli())) |
||||
} |
||||
} |
||||
|
||||
data class MoodEntryUiState( |
||||
val moodEntry: MoodEntryRepr |
||||
) |
||||
@ -0,0 +1,204 @@
@@ -0,0 +1,204 @@
|
||||
package ru.asakul.feel.ui.moodlist |
||||
|
||||
import androidx.compose.foundation.BorderStroke |
||||
import androidx.compose.foundation.background |
||||
import androidx.compose.foundation.border |
||||
import androidx.compose.foundation.layout.Arrangement |
||||
import androidx.compose.foundation.layout.Column |
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi |
||||
import androidx.compose.foundation.layout.FlowRow |
||||
import androidx.compose.foundation.layout.PaddingValues |
||||
import androidx.compose.foundation.layout.Row |
||||
import androidx.compose.foundation.layout.Spacer |
||||
import androidx.compose.foundation.layout.fillMaxWidth |
||||
import androidx.compose.foundation.layout.padding |
||||
import androidx.compose.foundation.layout.width |
||||
import androidx.compose.foundation.lazy.LazyColumn |
||||
import androidx.compose.foundation.lazy.items |
||||
import androidx.compose.foundation.lazy.rememberLazyListState |
||||
import androidx.compose.foundation.shape.CutCornerShape |
||||
import androidx.compose.material.icons.Icons |
||||
import androidx.compose.material.icons.filled.Add |
||||
import androidx.compose.material.icons.filled.Delete |
||||
import androidx.compose.material3.Card |
||||
import androidx.compose.material3.CardDefaults |
||||
import androidx.compose.material3.ExperimentalMaterial3Api |
||||
import androidx.compose.material3.FloatingActionButton |
||||
import androidx.compose.material3.Icon |
||||
import androidx.compose.material3.IconButton |
||||
import androidx.compose.material3.MaterialTheme |
||||
import androidx.compose.material3.Scaffold |
||||
import androidx.compose.material3.Surface |
||||
import androidx.compose.material3.Text |
||||
import androidx.compose.material3.TopAppBarDefaults |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.runtime.collectAsState |
||||
import androidx.compose.runtime.getValue |
||||
import androidx.compose.runtime.rememberCoroutineScope |
||||
import androidx.compose.ui.Alignment |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.graphics.Color |
||||
import androidx.compose.ui.graphics.Shape |
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll |
||||
import androidx.compose.ui.layout.VerticalAlignmentLine |
||||
import androidx.compose.ui.res.dimensionResource |
||||
import androidx.compose.ui.res.stringResource |
||||
import androidx.compose.ui.tooling.preview.Preview |
||||
import androidx.compose.ui.unit.dp |
||||
import androidx.lifecycle.viewmodel.compose.viewModel |
||||
import kotlinx.coroutines.launch |
||||
import ru.asakul.feel.AppViewModelProvider |
||||
import ru.asakul.feel.R |
||||
import ru.asakul.feel.ui.MoodEntryRepr |
||||
import ru.asakul.feel.ui.theme.AppTheme |
||||
import ru.asakul.feel.ui.theme.Shapes |
||||
import java.sql.Timestamp |
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class) |
||||
@Composable |
||||
fun MoodListScreen( |
||||
onNewMoodEntryClick: () -> Unit, |
||||
modifier: Modifier = Modifier, |
||||
viewModel: MoodListScreenViewModel = viewModel(factory = AppViewModelProvider.Factory) |
||||
) { |
||||
val moodListUiState by viewModel.moodEntryList.collectAsState() |
||||
val coroutineScope = rememberCoroutineScope() |
||||
|
||||
Scaffold( |
||||
modifier = modifier, |
||||
floatingActionButton = { |
||||
FloatingActionButton( |
||||
onClick = onNewMoodEntryClick, |
||||
shape = MaterialTheme.shapes.medium, |
||||
modifier = Modifier |
||||
.padding(20.dp) |
||||
) { |
||||
Icon( |
||||
imageVector = Icons.Default.Add, |
||||
contentDescription = stringResource(R.string.add_entry) |
||||
) |
||||
} |
||||
} |
||||
) { |
||||
innerPadding -> |
||||
MoodListBody( |
||||
onDeleteClicked = { |
||||
coroutineScope.launch { |
||||
viewModel.deleteMoodEntry(it) |
||||
} |
||||
}, |
||||
itemList = moodListUiState.moodEntryList, |
||||
contentPadding = innerPadding) |
||||
} |
||||
} |
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class) |
||||
@Composable |
||||
fun MoodListBody( |
||||
onDeleteClicked: (Long) -> Unit, |
||||
contentPadding: PaddingValues, |
||||
itemList: List<MoodEntryRepr>, |
||||
modifier: Modifier = Modifier) { |
||||
val listState = rememberLazyListState() |
||||
LazyColumn( |
||||
state = listState, |
||||
horizontalAlignment = Alignment.CenterHorizontally, |
||||
modifier = modifier |
||||
) { |
||||
items(items = itemList, key = { it.id }) { item -> |
||||
MoodListItem( |
||||
onDeleteClicked = onDeleteClicked, |
||||
item = item) |
||||
} |
||||
} |
||||
} |
||||
|
||||
@OptIn(ExperimentalLayoutApi::class) |
||||
@Composable |
||||
fun MoodListItem( |
||||
onDeleteClicked: (Long) -> Unit, |
||||
item: MoodEntryRepr, |
||||
modifier: Modifier = Modifier) { |
||||
Column( |
||||
modifier = modifier.padding(5.dp) |
||||
) { |
||||
Row( |
||||
modifier = Modifier.fillMaxWidth() |
||||
) { |
||||
Card( |
||||
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant), |
||||
border = BorderStroke(1.dp, Color.White), |
||||
modifier = Modifier.padding(1.dp), |
||||
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp) |
||||
) { |
||||
Row (modifier = Modifier.padding(10.dp), |
||||
verticalAlignment = Alignment.CenterVertically) { |
||||
Text(item.timestamp.toString(), style = MaterialTheme.typography.titleLarge) |
||||
Spacer(Modifier.weight(1f)) |
||||
Text("${item.moodLevel}", style = MaterialTheme.typography.titleLarge) |
||||
IconButton(onClick = {onDeleteClicked(item.id)}, |
||||
modifier = Modifier.width(32.dp)){ |
||||
Icon( |
||||
imageVector = Icons.Default.Delete, |
||||
contentDescription = stringResource(R.string.delete_entry) |
||||
) |
||||
} |
||||
} |
||||
Row { |
||||
FlowRow(modifier = Modifier.padding(12.dp)) { |
||||
for (emotion in item.emotions) { |
||||
TagItem( |
||||
text = emotion, |
||||
color = MaterialTheme.colorScheme.tertiaryContainer |
||||
) |
||||
} |
||||
|
||||
for (reason in item.reasons) { |
||||
TagItem( |
||||
text = reason, |
||||
color = MaterialTheme.colorScheme.primaryContainer |
||||
) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Composable |
||||
fun TagItem( |
||||
modifier: Modifier = Modifier, |
||||
text: String, |
||||
color: Color) { |
||||
Surface ( |
||||
shape = MaterialTheme.shapes.small, |
||||
modifier = modifier.padding(2.dp), |
||||
color = color, |
||||
border = BorderStroke(1.dp, MaterialTheme.colorScheme.outline) |
||||
) { |
||||
Text(text, modifier = |
||||
Modifier.padding(3.dp) |
||||
) |
||||
} |
||||
} |
||||
|
||||
@Preview(showBackground = true) |
||||
@Composable |
||||
fun MoodListPreview() { |
||||
AppTheme { |
||||
MoodListBody( |
||||
onDeleteClicked = {}, |
||||
contentPadding = PaddingValues(2.dp), |
||||
itemList = listOf( |
||||
MoodEntryRepr( |
||||
0, |
||||
Timestamp(12), |
||||
4, |
||||
listOf("Foo", "Bar"), |
||||
listOf("Bax", "Banana") |
||||
) |
||||
) |
||||
) |
||||
} |
||||
} |
||||
@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
|
||||
package ru.asakul.feel.ui.moodlist |
||||
|
||||
import androidx.lifecycle.ViewModel |
||||
import androidx.lifecycle.viewModelScope |
||||
import kotlinx.coroutines.flow.SharingStarted |
||||
import kotlinx.coroutines.flow.StateFlow |
||||
import kotlinx.coroutines.flow.first |
||||
import kotlinx.coroutines.flow.map |
||||
import kotlinx.coroutines.flow.stateIn |
||||
import ru.asakul.feel.data.MoodEntryRepository |
||||
import ru.asakul.feel.ui.MoodEntryRepr |
||||
import ru.asakul.feel.ui.toMoodEntryRepr |
||||
|
||||
class MoodListScreenViewModel(val moodEntryRepository: MoodEntryRepository) : ViewModel() { |
||||
val moodEntryList : StateFlow<MoodListScreenUiState> = moodEntryRepository |
||||
.getAllMoodEntriesStream().map { |
||||
MoodListScreenUiState(it.map { me -> me.toMoodEntryRepr() }) |
||||
}.stateIn( |
||||
scope = viewModelScope, |
||||
started = SharingStarted.WhileSubscribed(5000), |
||||
initialValue = MoodListScreenUiState() |
||||
) |
||||
|
||||
suspend fun deleteMoodEntry(id: Long) { |
||||
val item = moodEntryRepository.getMoodEntryStream(id).first() |
||||
if (item != null) { |
||||
moodEntryRepository.deleteMoodEntry(item) |
||||
} |
||||
} |
||||
} |
||||
|
||||
data class MoodListScreenUiState(val moodEntryList: List<MoodEntryRepr> = listOf()) |
||||
@ -0,0 +1,225 @@
@@ -0,0 +1,225 @@
|
||||
package ru.asakul.feel.ui.theme |
||||
import androidx.compose.ui.graphics.Color |
||||
|
||||
val primaryLight = Color(0xFF4E5B92) |
||||
val onPrimaryLight = Color(0xFFFFFFFF) |
||||
val primaryContainerLight = Color(0xFFDDE1FF) |
||||
val onPrimaryContainerLight = Color(0xFF364379) |
||||
val secondaryLight = Color(0xFF36693E) |
||||
val onSecondaryLight = Color(0xFFFFFFFF) |
||||
val secondaryContainerLight = Color(0xFFB7F1B9) |
||||
val onSecondaryContainerLight = Color(0xFF1D5128) |
||||
val tertiaryLight = Color(0xFF75546F) |
||||
val onTertiaryLight = Color(0xFFFFFFFF) |
||||
val tertiaryContainerLight = Color(0xFFFFD7F4) |
||||
val onTertiaryContainerLight = Color(0xFF5C3D56) |
||||
val errorLight = Color(0xFFBA1A1A) |
||||
val onErrorLight = Color(0xFFFFFFFF) |
||||
val errorContainerLight = Color(0xFFFFDAD6) |
||||
val onErrorContainerLight = Color(0xFF93000A) |
||||
val backgroundLight = Color(0xFFFBF8FF) |
||||
val onBackgroundLight = Color(0xFF1A1B21) |
||||
val surfaceLight = Color(0xFFFBF8FF) |
||||
val onSurfaceLight = Color(0xFF1A1B21) |
||||
val surfaceVariantLight = Color(0xFFE2E1EC) |
||||
val onSurfaceVariantLight = Color(0xFF45464F) |
||||
val outlineLight = Color(0xFF767680) |
||||
val outlineVariantLight = Color(0xFFC6C5D0) |
||||
val scrimLight = Color(0xFF000000) |
||||
val inverseSurfaceLight = Color(0xFF2F3036) |
||||
val inverseOnSurfaceLight = Color(0xFFF2F0F7) |
||||
val inversePrimaryLight = Color(0xFFB8C4FF) |
||||
val surfaceDimLight = Color(0xFFDBD9E0) |
||||
val surfaceBrightLight = Color(0xFFFBF8FF) |
||||
val surfaceContainerLowestLight = Color(0xFFFFFFFF) |
||||
val surfaceContainerLowLight = Color(0xFFF4F2FA) |
||||
val surfaceContainerLight = Color(0xFFEFEDF4) |
||||
val surfaceContainerHighLight = Color(0xFFE9E7EF) |
||||
val surfaceContainerHighestLight = Color(0xFFE3E1E9) |
||||
|
||||
val primaryLightMediumContrast = Color(0xFF253267) |
||||
val onPrimaryLightMediumContrast = Color(0xFFFFFFFF) |
||||
val primaryContainerLightMediumContrast = Color(0xFF5D6AA2) |
||||
val onPrimaryContainerLightMediumContrast = Color(0xFFFFFFFF) |
||||
val secondaryLightMediumContrast = Color(0xFF083F19) |
||||
val onSecondaryLightMediumContrast = Color(0xFFFFFFFF) |
||||
val secondaryContainerLightMediumContrast = Color(0xFF45784B) |
||||
val onSecondaryContainerLightMediumContrast = Color(0xFFFFFFFF) |
||||
val tertiaryLightMediumContrast = Color(0xFF4A2C45) |
||||
val onTertiaryLightMediumContrast = Color(0xFFFFFFFF) |
||||
val tertiaryContainerLightMediumContrast = Color(0xFF85627E) |
||||
val onTertiaryContainerLightMediumContrast = Color(0xFFFFFFFF) |
||||
val errorLightMediumContrast = Color(0xFF740006) |
||||
val onErrorLightMediumContrast = Color(0xFFFFFFFF) |
||||
val errorContainerLightMediumContrast = Color(0xFFCF2C27) |
||||
val onErrorContainerLightMediumContrast = Color(0xFFFFFFFF) |
||||
val backgroundLightMediumContrast = Color(0xFFFBF8FF) |
||||
val onBackgroundLightMediumContrast = Color(0xFF1A1B21) |
||||
val surfaceLightMediumContrast = Color(0xFFFBF8FF) |
||||
val onSurfaceLightMediumContrast = Color(0xFF101116) |
||||
val surfaceVariantLightMediumContrast = Color(0xFFE2E1EC) |
||||
val onSurfaceVariantLightMediumContrast = Color(0xFF35363E) |
||||
val outlineLightMediumContrast = Color(0xFF51525B) |
||||
val outlineVariantLightMediumContrast = Color(0xFF6C6C76) |
||||
val scrimLightMediumContrast = Color(0xFF000000) |
||||
val inverseSurfaceLightMediumContrast = Color(0xFF2F3036) |
||||
val inverseOnSurfaceLightMediumContrast = Color(0xFFF2F0F7) |
||||
val inversePrimaryLightMediumContrast = Color(0xFFB8C4FF) |
||||
val surfaceDimLightMediumContrast = Color(0xFFC7C5CD) |
||||
val surfaceBrightLightMediumContrast = Color(0xFFFBF8FF) |
||||
val surfaceContainerLowestLightMediumContrast = Color(0xFFFFFFFF) |
||||
val surfaceContainerLowLightMediumContrast = Color(0xFFF4F2FA) |
||||
val surfaceContainerLightMediumContrast = Color(0xFFE9E7EF) |
||||
val surfaceContainerHighLightMediumContrast = Color(0xFFDDDCE3) |
||||
val surfaceContainerHighestLightMediumContrast = Color(0xFFD2D1D8) |
||||
|
||||
val primaryLightHighContrast = Color(0xFF1A285C) |
||||
val onPrimaryLightHighContrast = Color(0xFFFFFFFF) |
||||
val primaryContainerLightHighContrast = Color(0xFF39467B) |
||||
val onPrimaryContainerLightHighContrast = Color(0xFFFFFFFF) |
||||
val secondaryLightHighContrast = Color(0xFF003411) |
||||
val onSecondaryLightHighContrast = Color(0xFFFFFFFF) |
||||
val secondaryContainerLightHighContrast = Color(0xFF20532A) |
||||
val onSecondaryContainerLightHighContrast = Color(0xFFFFFFFF) |
||||
val tertiaryLightHighContrast = Color(0xFF3F223A) |
||||
val onTertiaryLightHighContrast = Color(0xFFFFFFFF) |
||||
val tertiaryContainerLightHighContrast = Color(0xFF5E3F59) |
||||
val onTertiaryContainerLightHighContrast = Color(0xFFFFFFFF) |
||||
val errorLightHighContrast = Color(0xFF600004) |
||||
val onErrorLightHighContrast = Color(0xFFFFFFFF) |
||||
val errorContainerLightHighContrast = Color(0xFF98000A) |
||||
val onErrorContainerLightHighContrast = Color(0xFFFFFFFF) |
||||
val backgroundLightHighContrast = Color(0xFFFBF8FF) |
||||
val onBackgroundLightHighContrast = Color(0xFF1A1B21) |
||||
val surfaceLightHighContrast = Color(0xFFFBF8FF) |
||||
val onSurfaceLightHighContrast = Color(0xFF000000) |
||||
val surfaceVariantLightHighContrast = Color(0xFFE2E1EC) |
||||
val onSurfaceVariantLightHighContrast = Color(0xFF000000) |
||||
val outlineLightHighContrast = Color(0xFF2A2C34) |
||||
val outlineVariantLightHighContrast = Color(0xFF484951) |
||||
val scrimLightHighContrast = Color(0xFF000000) |
||||
val inverseSurfaceLightHighContrast = Color(0xFF2F3036) |
||||
val inverseOnSurfaceLightHighContrast = Color(0xFFFFFFFF) |
||||
val inversePrimaryLightHighContrast = Color(0xFFB8C4FF) |
||||
val surfaceDimLightHighContrast = Color(0xFFB9B8BF) |
||||
val surfaceBrightLightHighContrast = Color(0xFFFBF8FF) |
||||
val surfaceContainerLowestLightHighContrast = Color(0xFFFFFFFF) |
||||
val surfaceContainerLowLightHighContrast = Color(0xFFF2F0F7) |
||||
val surfaceContainerLightHighContrast = Color(0xFFE3E1E9) |
||||
val surfaceContainerHighLightHighContrast = Color(0xFFD5D3DB) |
||||
val surfaceContainerHighestLightHighContrast = Color(0xFFC7C5CD) |
||||
|
||||
val primaryDark = Color(0xFFB8C4FF) |
||||
val onPrimaryDark = Color(0xFF1F2D61) |
||||
val primaryContainerDark = Color(0xFF364379) |
||||
val onPrimaryContainerDark = Color(0xFFDDE1FF) |
||||
val secondaryDark = Color(0xFF9CD49F) |
||||
val onSecondaryDark = Color(0xFF003913) |
||||
val secondaryContainerDark = Color(0xFF1D5128) |
||||
val onSecondaryContainerDark = Color(0xFFB7F1B9) |
||||
val tertiaryDark = Color(0xFFE4BAD9) |
||||
val onTertiaryDark = Color(0xFF43273F) |
||||
val tertiaryContainerDark = Color(0xFF5C3D56) |
||||
val onTertiaryContainerDark = Color(0xFFFFD7F4) |
||||
val errorDark = Color(0xFFFFB4AB) |
||||
val onErrorDark = Color(0xFF690005) |
||||
val errorContainerDark = Color(0xFF93000A) |
||||
val onErrorContainerDark = Color(0xFFFFDAD6) |
||||
val backgroundDark = Color(0xFF121318) |
||||
val onBackgroundDark = Color(0xFFE3E1E9) |
||||
val surfaceDark = Color(0xFF121318) |
||||
val onSurfaceDark = Color(0xFFE3E1E9) |
||||
val surfaceVariantDark = Color(0xFF45464F) |
||||
val onSurfaceVariantDark = Color(0xFFC6C5D0) |
||||
val outlineDark = Color(0xFF90909A) |
||||
val outlineVariantDark = Color(0xFF45464F) |
||||
val scrimDark = Color(0xFF000000) |
||||
val inverseSurfaceDark = Color(0xFFE3E1E9) |
||||
val inverseOnSurfaceDark = Color(0xFF2F3036) |
||||
val inversePrimaryDark = Color(0xFF4E5B92) |
||||
val surfaceDimDark = Color(0xFF121318) |
||||
val surfaceBrightDark = Color(0xFF38393F) |
||||
val surfaceContainerLowestDark = Color(0xFF0D0E13) |
||||
val surfaceContainerLowDark = Color(0xFF1A1B21) |
||||
val surfaceContainerDark = Color(0xFF1F1F25) |
||||
val surfaceContainerHighDark = Color(0xFF292A2F) |
||||
val surfaceContainerHighestDark = Color(0xFF34343A) |
||||
|
||||
val primaryDarkMediumContrast = Color(0xFFD5DAFF) |
||||
val onPrimaryDarkMediumContrast = Color(0xFF132155) |
||||
val primaryContainerDarkMediumContrast = Color(0xFF818EC8) |
||||
val onPrimaryContainerDarkMediumContrast = Color(0xFF000000) |
||||
val secondaryDarkMediumContrast = Color(0xFFB1EAB3) |
||||
val onSecondaryDarkMediumContrast = Color(0xFF002D0D) |
||||
val secondaryContainerDarkMediumContrast = Color(0xFF689D6C) |
||||
val onSecondaryContainerDarkMediumContrast = Color(0xFF000000) |
||||
val tertiaryDarkMediumContrast = Color(0xFFFBD0EF) |
||||
val onTertiaryDarkMediumContrast = Color(0xFF381C34) |
||||
val tertiaryContainerDarkMediumContrast = Color(0xFFAB85A2) |
||||
val onTertiaryContainerDarkMediumContrast = Color(0xFF000000) |
||||
val errorDarkMediumContrast = Color(0xFFFFD2CC) |
||||
val onErrorDarkMediumContrast = Color(0xFF540003) |
||||
val errorContainerDarkMediumContrast = Color(0xFFFF5449) |
||||
val onErrorContainerDarkMediumContrast = Color(0xFF000000) |
||||
val backgroundDarkMediumContrast = Color(0xFF121318) |
||||
val onBackgroundDarkMediumContrast = Color(0xFFE3E1E9) |
||||
val surfaceDarkMediumContrast = Color(0xFF121318) |
||||
val onSurfaceDarkMediumContrast = Color(0xFFFFFFFF) |
||||
val surfaceVariantDarkMediumContrast = Color(0xFF45464F) |
||||
val onSurfaceVariantDarkMediumContrast = Color(0xFFDCDBE6) |
||||
val outlineDarkMediumContrast = Color(0xFFB1B1BB) |
||||
val outlineVariantDarkMediumContrast = Color(0xFF8F8F99) |
||||
val scrimDarkMediumContrast = Color(0xFF000000) |
||||
val inverseSurfaceDarkMediumContrast = Color(0xFFE3E1E9) |
||||
val inverseOnSurfaceDarkMediumContrast = Color(0xFF292A2F) |
||||
val inversePrimaryDarkMediumContrast = Color(0xFF38457A) |
||||
val surfaceDimDarkMediumContrast = Color(0xFF121318) |
||||
val surfaceBrightDarkMediumContrast = Color(0xFF44444A) |
||||
val surfaceContainerLowestDarkMediumContrast = Color(0xFF06070C) |
||||
val surfaceContainerLowDarkMediumContrast = Color(0xFF1D1D23) |
||||
val surfaceContainerDarkMediumContrast = Color(0xFF27272D) |
||||
val surfaceContainerHighDarkMediumContrast = Color(0xFF323238) |
||||
val surfaceContainerHighestDarkMediumContrast = Color(0xFF3D3D43) |
||||
|
||||
val primaryDarkHighContrast = Color(0xFFEEEFFF) |
||||
val onPrimaryDarkHighContrast = Color(0xFF000000) |
||||
val primaryContainerDarkHighContrast = Color(0xFFB3C0FD) |
||||
val onPrimaryContainerDarkHighContrast = Color(0xFF00072D) |
||||
val secondaryDarkHighContrast = Color(0xFFC5FEC6) |
||||
val onSecondaryDarkHighContrast = Color(0xFF000000) |
||||
val secondaryContainerDarkHighContrast = Color(0xFF98D09B) |
||||
val onSecondaryContainerDarkHighContrast = Color(0xFF000F02) |
||||
val tertiaryDarkHighContrast = Color(0xFFFFEAF7) |
||||
val onTertiaryDarkHighContrast = Color(0xFF000000) |
||||
val tertiaryContainerDarkHighContrast = Color(0xFFE0B6D5) |
||||
val onTertiaryContainerDarkHighContrast = Color(0xFF190318) |
||||
val errorDarkHighContrast = Color(0xFFFFECE9) |
||||
val onErrorDarkHighContrast = Color(0xFF000000) |
||||
val errorContainerDarkHighContrast = Color(0xFFFFAEA4) |
||||
val onErrorContainerDarkHighContrast = Color(0xFF220001) |
||||
val backgroundDarkHighContrast = Color(0xFF121318) |
||||
val onBackgroundDarkHighContrast = Color(0xFFE3E1E9) |
||||
val surfaceDarkHighContrast = Color(0xFF121318) |
||||
val onSurfaceDarkHighContrast = Color(0xFFFFFFFF) |
||||
val surfaceVariantDarkHighContrast = Color(0xFF45464F) |
||||
val onSurfaceVariantDarkHighContrast = Color(0xFFFFFFFF) |
||||
val outlineDarkHighContrast = Color(0xFFF0EFFA) |
||||
val outlineVariantDarkHighContrast = Color(0xFFC2C2CC) |
||||
val scrimDarkHighContrast = Color(0xFF000000) |
||||
val inverseSurfaceDarkHighContrast = Color(0xFFE3E1E9) |
||||
val inverseOnSurfaceDarkHighContrast = Color(0xFF000000) |
||||
val inversePrimaryDarkHighContrast = Color(0xFF38457A) |
||||
val surfaceDimDarkHighContrast = Color(0xFF121318) |
||||
val surfaceBrightDarkHighContrast = Color(0xFF4F4F56) |
||||
val surfaceContainerLowestDarkHighContrast = Color(0xFF000000) |
||||
val surfaceContainerLowDarkHighContrast = Color(0xFF1F1F25) |
||||
val surfaceContainerDarkHighContrast = Color(0xFF2F3036) |
||||
val surfaceContainerHighDarkHighContrast = Color(0xFF3B3B41) |
||||
val surfaceContainerHighestDarkHighContrast = Color(0xFF46464C) |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,12 @@
@@ -0,0 +1,12 @@
|
||||
package ru.asakul.feel.ui.theme |
||||
|
||||
import androidx.compose.foundation.shape.CutCornerShape |
||||
import androidx.compose.material3.Shapes |
||||
import androidx.compose.ui.unit.dp |
||||
|
||||
val Shapes = Shapes( |
||||
|
||||
extraSmall = CutCornerShape(topEnd = 8.dp, bottomStart = 8.dp), |
||||
small = CutCornerShape(topEnd = 8.dp, bottomStart = 8.dp), |
||||
medium = CutCornerShape(topEnd = 16.dp, bottomStart = 16.dp) |
||||
) |
||||
@ -0,0 +1,282 @@
@@ -0,0 +1,282 @@
|
||||
package ru.asakul.feel.ui.theme |
||||
import android.app.Activity |
||||
import android.os.Build |
||||
import androidx.compose.foundation.isSystemInDarkTheme |
||||
import androidx.compose.material3.MaterialTheme |
||||
import androidx.compose.material3.lightColorScheme |
||||
import androidx.compose.material3.darkColorScheme |
||||
import androidx.compose.material3.dynamicDarkColorScheme |
||||
import androidx.compose.material3.dynamicLightColorScheme |
||||
import androidx.compose.material3.Typography |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.runtime.Immutable |
||||
import androidx.compose.ui.graphics.Color |
||||
import androidx.compose.ui.graphics.toArgb |
||||
import androidx.compose.ui.platform.LocalContext |
||||
import ru.asakul.feel.ui.theme.AppTypography |
||||
|
||||
private val lightScheme = lightColorScheme( |
||||
primary = primaryLight, |
||||
onPrimary = onPrimaryLight, |
||||
primaryContainer = primaryContainerLight, |
||||
onPrimaryContainer = onPrimaryContainerLight, |
||||
secondary = secondaryLight, |
||||
onSecondary = onSecondaryLight, |
||||
secondaryContainer = secondaryContainerLight, |
||||
onSecondaryContainer = onSecondaryContainerLight, |
||||
tertiary = tertiaryLight, |
||||
onTertiary = onTertiaryLight, |
||||
tertiaryContainer = tertiaryContainerLight, |
||||
onTertiaryContainer = onTertiaryContainerLight, |
||||
error = errorLight, |
||||
onError = onErrorLight, |
||||
errorContainer = errorContainerLight, |
||||
onErrorContainer = onErrorContainerLight, |
||||
background = backgroundLight, |
||||
onBackground = onBackgroundLight, |
||||
surface = surfaceLight, |
||||
onSurface = onSurfaceLight, |
||||
surfaceVariant = surfaceVariantLight, |
||||
onSurfaceVariant = onSurfaceVariantLight, |
||||
outline = outlineLight, |
||||
outlineVariant = outlineVariantLight, |
||||
scrim = scrimLight, |
||||
inverseSurface = inverseSurfaceLight, |
||||
inverseOnSurface = inverseOnSurfaceLight, |
||||
inversePrimary = inversePrimaryLight, |
||||
surfaceDim = surfaceDimLight, |
||||
surfaceBright = surfaceBrightLight, |
||||
surfaceContainerLowest = surfaceContainerLowestLight, |
||||
surfaceContainerLow = surfaceContainerLowLight, |
||||
surfaceContainer = surfaceContainerLight, |
||||
surfaceContainerHigh = surfaceContainerHighLight, |
||||
surfaceContainerHighest = surfaceContainerHighestLight, |
||||
) |
||||
|
||||
private val darkScheme = darkColorScheme( |
||||
primary = primaryDark, |
||||
onPrimary = onPrimaryDark, |
||||
primaryContainer = primaryContainerDark, |
||||
onPrimaryContainer = onPrimaryContainerDark, |
||||
secondary = secondaryDark, |
||||
onSecondary = onSecondaryDark, |
||||
secondaryContainer = secondaryContainerDark, |
||||
onSecondaryContainer = onSecondaryContainerDark, |
||||
tertiary = tertiaryDark, |
||||
onTertiary = onTertiaryDark, |
||||
tertiaryContainer = tertiaryContainerDark, |
||||
onTertiaryContainer = onTertiaryContainerDark, |
||||
error = errorDark, |
||||
onError = onErrorDark, |
||||
errorContainer = errorContainerDark, |
||||
onErrorContainer = onErrorContainerDark, |
||||
background = backgroundDark, |
||||
onBackground = onBackgroundDark, |
||||
surface = surfaceDark, |
||||
onSurface = onSurfaceDark, |
||||
surfaceVariant = surfaceVariantDark, |
||||
onSurfaceVariant = onSurfaceVariantDark, |
||||
outline = outlineDark, |
||||
outlineVariant = outlineVariantDark, |
||||
scrim = scrimDark, |
||||
inverseSurface = inverseSurfaceDark, |
||||
inverseOnSurface = inverseOnSurfaceDark, |
||||
inversePrimary = inversePrimaryDark, |
||||
surfaceDim = surfaceDimDark, |
||||
surfaceBright = surfaceBrightDark, |
||||
surfaceContainerLowest = surfaceContainerLowestDark, |
||||
surfaceContainerLow = surfaceContainerLowDark, |
||||
surfaceContainer = surfaceContainerDark, |
||||
surfaceContainerHigh = surfaceContainerHighDark, |
||||
surfaceContainerHighest = surfaceContainerHighestDark, |
||||
) |
||||
|
||||
private val mediumContrastLightColorScheme = lightColorScheme( |
||||
primary = primaryLightMediumContrast, |
||||
onPrimary = onPrimaryLightMediumContrast, |
||||
primaryContainer = primaryContainerLightMediumContrast, |
||||
onPrimaryContainer = onPrimaryContainerLightMediumContrast, |
||||
secondary = secondaryLightMediumContrast, |
||||
onSecondary = onSecondaryLightMediumContrast, |
||||
secondaryContainer = secondaryContainerLightMediumContrast, |
||||
onSecondaryContainer = onSecondaryContainerLightMediumContrast, |
||||
tertiary = tertiaryLightMediumContrast, |
||||
onTertiary = onTertiaryLightMediumContrast, |
||||
tertiaryContainer = tertiaryContainerLightMediumContrast, |
||||
onTertiaryContainer = onTertiaryContainerLightMediumContrast, |
||||
error = errorLightMediumContrast, |
||||
onError = onErrorLightMediumContrast, |
||||
errorContainer = errorContainerLightMediumContrast, |
||||
onErrorContainer = onErrorContainerLightMediumContrast, |
||||
background = backgroundLightMediumContrast, |
||||
onBackground = onBackgroundLightMediumContrast, |
||||
surface = surfaceLightMediumContrast, |
||||
onSurface = onSurfaceLightMediumContrast, |
||||
surfaceVariant = surfaceVariantLightMediumContrast, |
||||
onSurfaceVariant = onSurfaceVariantLightMediumContrast, |
||||
outline = outlineLightMediumContrast, |
||||
outlineVariant = outlineVariantLightMediumContrast, |
||||
scrim = scrimLightMediumContrast, |
||||
inverseSurface = inverseSurfaceLightMediumContrast, |
||||
inverseOnSurface = inverseOnSurfaceLightMediumContrast, |
||||
inversePrimary = inversePrimaryLightMediumContrast, |
||||
surfaceDim = surfaceDimLightMediumContrast, |
||||
surfaceBright = surfaceBrightLightMediumContrast, |
||||
surfaceContainerLowest = surfaceContainerLowestLightMediumContrast, |
||||
surfaceContainerLow = surfaceContainerLowLightMediumContrast, |
||||
surfaceContainer = surfaceContainerLightMediumContrast, |
||||
surfaceContainerHigh = surfaceContainerHighLightMediumContrast, |
||||
surfaceContainerHighest = surfaceContainerHighestLightMediumContrast, |
||||
) |
||||
|
||||
private val highContrastLightColorScheme = lightColorScheme( |
||||
primary = primaryLightHighContrast, |
||||
onPrimary = onPrimaryLightHighContrast, |
||||
primaryContainer = primaryContainerLightHighContrast, |
||||
onPrimaryContainer = onPrimaryContainerLightHighContrast, |
||||
secondary = secondaryLightHighContrast, |
||||
onSecondary = onSecondaryLightHighContrast, |
||||
secondaryContainer = secondaryContainerLightHighContrast, |
||||
onSecondaryContainer = onSecondaryContainerLightHighContrast, |
||||
tertiary = tertiaryLightHighContrast, |
||||
onTertiary = onTertiaryLightHighContrast, |
||||
tertiaryContainer = tertiaryContainerLightHighContrast, |
||||
onTertiaryContainer = onTertiaryContainerLightHighContrast, |
||||
error = errorLightHighContrast, |
||||
onError = onErrorLightHighContrast, |
||||
errorContainer = errorContainerLightHighContrast, |
||||
onErrorContainer = onErrorContainerLightHighContrast, |
||||
background = backgroundLightHighContrast, |
||||
onBackground = onBackgroundLightHighContrast, |
||||
surface = surfaceLightHighContrast, |
||||
onSurface = onSurfaceLightHighContrast, |
||||
surfaceVariant = surfaceVariantLightHighContrast, |
||||
onSurfaceVariant = onSurfaceVariantLightHighContrast, |
||||
outline = outlineLightHighContrast, |
||||
outlineVariant = outlineVariantLightHighContrast, |
||||
scrim = scrimLightHighContrast, |
||||
inverseSurface = inverseSurfaceLightHighContrast, |
||||
inverseOnSurface = inverseOnSurfaceLightHighContrast, |
||||
inversePrimary = inversePrimaryLightHighContrast, |
||||
surfaceDim = surfaceDimLightHighContrast, |
||||
surfaceBright = surfaceBrightLightHighContrast, |
||||
surfaceContainerLowest = surfaceContainerLowestLightHighContrast, |
||||
surfaceContainerLow = surfaceContainerLowLightHighContrast, |
||||
surfaceContainer = surfaceContainerLightHighContrast, |
||||
surfaceContainerHigh = surfaceContainerHighLightHighContrast, |
||||
surfaceContainerHighest = surfaceContainerHighestLightHighContrast, |
||||
) |
||||
|
||||
private val mediumContrastDarkColorScheme = darkColorScheme( |
||||
primary = primaryDarkMediumContrast, |
||||
onPrimary = onPrimaryDarkMediumContrast, |
||||
primaryContainer = primaryContainerDarkMediumContrast, |
||||
onPrimaryContainer = onPrimaryContainerDarkMediumContrast, |
||||
secondary = secondaryDarkMediumContrast, |
||||
onSecondary = onSecondaryDarkMediumContrast, |
||||
secondaryContainer = secondaryContainerDarkMediumContrast, |
||||
onSecondaryContainer = onSecondaryContainerDarkMediumContrast, |
||||
tertiary = tertiaryDarkMediumContrast, |
||||
onTertiary = onTertiaryDarkMediumContrast, |
||||
tertiaryContainer = tertiaryContainerDarkMediumContrast, |
||||
onTertiaryContainer = onTertiaryContainerDarkMediumContrast, |
||||
error = errorDarkMediumContrast, |
||||
onError = onErrorDarkMediumContrast, |
||||
errorContainer = errorContainerDarkMediumContrast, |
||||
onErrorContainer = onErrorContainerDarkMediumContrast, |
||||
background = backgroundDarkMediumContrast, |
||||
onBackground = onBackgroundDarkMediumContrast, |
||||
surface = surfaceDarkMediumContrast, |
||||
onSurface = onSurfaceDarkMediumContrast, |
||||
surfaceVariant = surfaceVariantDarkMediumContrast, |
||||
onSurfaceVariant = onSurfaceVariantDarkMediumContrast, |
||||
outline = outlineDarkMediumContrast, |
||||
outlineVariant = outlineVariantDarkMediumContrast, |
||||
scrim = scrimDarkMediumContrast, |
||||
inverseSurface = inverseSurfaceDarkMediumContrast, |
||||
inverseOnSurface = inverseOnSurfaceDarkMediumContrast, |
||||
inversePrimary = inversePrimaryDarkMediumContrast, |
||||
surfaceDim = surfaceDimDarkMediumContrast, |
||||
surfaceBright = surfaceBrightDarkMediumContrast, |
||||
surfaceContainerLowest = surfaceContainerLowestDarkMediumContrast, |
||||
surfaceContainerLow = surfaceContainerLowDarkMediumContrast, |
||||
surfaceContainer = surfaceContainerDarkMediumContrast, |
||||
surfaceContainerHigh = surfaceContainerHighDarkMediumContrast, |
||||
surfaceContainerHighest = surfaceContainerHighestDarkMediumContrast, |
||||
) |
||||
|
||||
private val highContrastDarkColorScheme = darkColorScheme( |
||||
primary = primaryDarkHighContrast, |
||||
onPrimary = onPrimaryDarkHighContrast, |
||||
primaryContainer = primaryContainerDarkHighContrast, |
||||
onPrimaryContainer = onPrimaryContainerDarkHighContrast, |
||||
secondary = secondaryDarkHighContrast, |
||||
onSecondary = onSecondaryDarkHighContrast, |
||||
secondaryContainer = secondaryContainerDarkHighContrast, |
||||
onSecondaryContainer = onSecondaryContainerDarkHighContrast, |
||||
tertiary = tertiaryDarkHighContrast, |
||||
onTertiary = onTertiaryDarkHighContrast, |
||||
tertiaryContainer = tertiaryContainerDarkHighContrast, |
||||
onTertiaryContainer = onTertiaryContainerDarkHighContrast, |
||||
error = errorDarkHighContrast, |
||||
onError = onErrorDarkHighContrast, |
||||
errorContainer = errorContainerDarkHighContrast, |
||||
onErrorContainer = onErrorContainerDarkHighContrast, |
||||
background = backgroundDarkHighContrast, |
||||
onBackground = onBackgroundDarkHighContrast, |
||||
surface = surfaceDarkHighContrast, |
||||
onSurface = onSurfaceDarkHighContrast, |
||||
surfaceVariant = surfaceVariantDarkHighContrast, |
||||
onSurfaceVariant = onSurfaceVariantDarkHighContrast, |
||||
outline = outlineDarkHighContrast, |
||||
outlineVariant = outlineVariantDarkHighContrast, |
||||
scrim = scrimDarkHighContrast, |
||||
inverseSurface = inverseSurfaceDarkHighContrast, |
||||
inverseOnSurface = inverseOnSurfaceDarkHighContrast, |
||||
inversePrimary = inversePrimaryDarkHighContrast, |
||||
surfaceDim = surfaceDimDarkHighContrast, |
||||
surfaceBright = surfaceBrightDarkHighContrast, |
||||
surfaceContainerLowest = surfaceContainerLowestDarkHighContrast, |
||||
surfaceContainerLow = surfaceContainerLowDarkHighContrast, |
||||
surfaceContainer = surfaceContainerDarkHighContrast, |
||||
surfaceContainerHigh = surfaceContainerHighDarkHighContrast, |
||||
surfaceContainerHighest = surfaceContainerHighestDarkHighContrast, |
||||
) |
||||
|
||||
@Immutable |
||||
data class ColorFamily( |
||||
val color: Color, |
||||
val onColor: Color, |
||||
val colorContainer: Color, |
||||
val onColorContainer: Color |
||||
) |
||||
|
||||
val unspecified_scheme = ColorFamily( |
||||
Color.Unspecified, Color.Unspecified, Color.Unspecified, Color.Unspecified |
||||
) |
||||
|
||||
@Composable |
||||
fun AppTheme( |
||||
darkTheme: Boolean = isSystemInDarkTheme(), |
||||
// Dynamic color is available on Android 12+ |
||||
dynamicColor: Boolean = true, |
||||
content: @Composable() () -> Unit |
||||
) { |
||||
val colorScheme = when { |
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { |
||||
val context = LocalContext.current |
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) |
||||
} |
||||
|
||||
darkTheme -> darkScheme |
||||
else -> lightScheme |
||||
} |
||||
|
||||
MaterialTheme( |
||||
colorScheme = colorScheme, |
||||
typography = AppTypography, |
||||
content = content, |
||||
shapes = Shapes |
||||
) |
||||
} |
||||
|
||||
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
package ru.asakul.feel.ui.theme |
||||
|
||||
import androidx.compose.material3.Typography |
||||
|
||||
val AppTypography = Typography() |
||||
@ -0,0 +1,170 @@
@@ -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> |
||||
@ -0,0 +1,30 @@
@@ -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> |
||||
@ -0,0 +1,6 @@
@@ -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> |
||||
@ -0,0 +1,6 @@
@@ -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> |
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 982 B |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<resources> |
||||
<string name="app_name">Feel</string> |
||||
<string name="how_s_your_mood_right_now">Как твое настроение сейчас?</string> |
||||
<string name="what_do_you_feel">Что ты чувствуешь?</string> |
||||
<string name="why_do_you_feel_that_way">Почему ты это чувствуешь?</string> |
||||
<string name="happiness">Счастье</string> |
||||
<string name="elation">Восторг</string> |
||||
<string name="calmness">Спокойствие</string> |
||||
<string name="done">Готово</string> |
||||
<string name="tranquility">Спокойствие</string> |
||||
<string name="gratitude">Благодарность</string> |
||||
<string name="surprise">Удивление</string> |
||||
<string name="fatigue">Усталость</string> |
||||
<string name="exhaustion">Истощение</string> |
||||
<string name="anger">Гнев</string> |
||||
<string name="sadness">Грусть</string> |
||||
<string name="fear">Страх</string> |
||||
<string name="panic">Паника</string> |
||||
<string name="irritation">Разрдражение</string> |
||||
<string name="apathy">Апатия</string> |
||||
<string name="hope">Надежда</string> |
||||
<string name="relief">Облегчение</string> |
||||
<string name="confidence">Уверенность</string> |
||||
<string name="satisfaction">Удовлетворение</string> |
||||
<string name="joy">Радость</string> |
||||
<string name="enthusiasm">Энтузиазм</string> |
||||
<string name="pride">Гордость</string> |
||||
<string name="anxiety">Беспокойство</string> |
||||
<string name="nervousness">Нервозность</string> |
||||
<string name="boldness">Смелость</string> |
||||
<string name="disgust">Отвращение</string> |
||||
<string name="jealousy">Ревность</string> |
||||
<string name="guilt">Вина</string> |
||||
<string name="embarrassment">Смущение</string> |
||||
<string name="disenchantment">Разочарование</string> |
||||
<string name="stress">Стресс</string> |
||||
<string name="loneliness">Одиночество</string> |
||||
<string name="home">Дом</string> |
||||
<string name="work">Работа</string> |
||||
<string name="hobby">Хобби</string> |
||||
<string name="family">Семья</string> |
||||
<string name="society">Общество</string> |
||||
<string name="exercise">Упражнения</string> |
||||
<string name="politics">Политика</string> |
||||
<string name="weather">Погода</string> |
||||
<string name="health">Здоровье</string> |
||||
<string name="sleep">Сон</string> |
||||
<string name="food">Еда</string> |
||||
<string name="money">Деньги</string> |
||||
<string name="add_entry">Add entry</string> |
||||
<string name="delete_entry">Delete entry</string> |
||||
</resources> |
||||
@ -0,0 +1,10 @@
@@ -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> |
||||
@ -0,0 +1,52 @@
@@ -0,0 +1,52 @@
|
||||
<resources> |
||||
<string name="app_name">Feel</string> |
||||
<string name="how_s_your_mood_right_now">How\'s your mood right now?</string> |
||||
<string name="what_do_you_feel">What do you feel?</string> |
||||
<string name="why_do_you_feel_that_way">Why do you feel that way?</string> |
||||
<string name="happiness">Happiness</string> |
||||
<string name="elation">Elation</string> |
||||
<string name="calmness">Calmness</string> |
||||
<string name="done">Done</string> |
||||
<string name="tranquility">Tranquility</string> |
||||
<string name="gratitude">Gratitude</string> |
||||
<string name="surprise">Surprise</string> |
||||
<string name="fatigue">Fatigue</string> |
||||
<string name="exhaustion">Exhaustion</string> |
||||
<string name="anger">Anger</string> |
||||
<string name="sadness">Sadness</string> |
||||
<string name="fear">Fear</string> |
||||
<string name="panic">Panic</string> |
||||
<string name="irritation">Irritation</string> |
||||
<string name="apathy">Apathy</string> |
||||
<string name="hope">Hope</string> |
||||
<string name="relief">Relief</string> |
||||
<string name="confidence">Confidence</string> |
||||
<string name="satisfaction">Satisfaction</string> |
||||
<string name="joy">Joy</string> |
||||
<string name="enthusiasm">Enthusiasm</string> |
||||
<string name="pride">Pride</string> |
||||
<string name="anxiety">Anxiety</string> |
||||
<string name="nervousness">Nervousness</string> |
||||
<string name="boldness">Boldness</string> |
||||
<string name="disgust">Disgust</string> |
||||
<string name="jealousy">Jealousy</string> |
||||
<string name="guilt">Guilt</string> |
||||
<string name="embarrassment">Embarrassment</string> |
||||
<string name="disenchantment">Disenchantment</string> |
||||
<string name="stress">Stress</string> |
||||
<string name="loneliness">Loneliness</string> |
||||
<string name="home">Home</string> |
||||
<string name="work">Work</string> |
||||
<string name="hobby">Hobby</string> |
||||
<string name="family">Family</string> |
||||
<string name="society">Society</string> |
||||
<string name="exercise">Exercise</string> |
||||
<string name="politics">Politics</string> |
||||
<string name="weather">Weather</string> |
||||
<string name="health">Health</string> |
||||
<string name="sleep">Sleep</string> |
||||
<string name="food">Food</string> |
||||
<string name="money">Money</string> |
||||
<string name="add_entry">Add entry</string> |
||||
<string name="delete_entry">Delete entry</string> |
||||
</resources> |
||||
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<resources> |
||||
|
||||
<style name="Theme.Feel" parent="android:Theme.Material.Light.NoActionBar" /> |
||||
</resources> |
||||
@ -0,0 +1,13 @@
@@ -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 than API 31 |
||||
See https://developer.android.com/about/versions/12/backup-restore |
||||
--> |
||||
<full-backup-content> |
||||
<!-- |
||||
<include domain="sharedpref" path="."/> |
||||
<exclude domain="sharedpref" path="device.xml"/> |
||||
--> |
||||
</full-backup-content> |
||||
@ -0,0 +1,19 @@
@@ -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> |
||||
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
package ru.asakul.feel |
||||
|
||||
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) |
||||
} |
||||
} |
||||