azea/AddEditBio #4

Merged
Azea_Avenbright merged 5 commits from azea/AddEditBio into ophelia_mainline 2023-10-20 12:43:34 -04:00
22 changed files with 765 additions and 116 deletions

View file

@ -59,7 +59,7 @@ dependencies {
implementation 'androidx.compose.ui:ui'
implementation 'androidx.compose.ui:ui-graphics'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.material3:material3'
implementation 'androidx.compose.material3:material3:1.1.2'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
@ -71,12 +71,19 @@ dependencies {
implementation 'androidx.room:room-ktx:2.5.2'
implementation 'androidx.navigation:navigation-runtime-ktx:2.7.4'
implementation('androidx.constraintlayout:constraintlayout-compose-android:1.1.0-alpha13')
def lifecycle_version = "2.6.2"
def arch_version = "2.2.0"
def roomVersion = '2.3.0'
implementation 'androidx.core:core-splashscreen:1.0.1'
def nav_version = "2.7.4"
implementation "androidx.navigation:navigation-compose:$nav_version"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"

View file

@ -1,70 +1,22 @@
package com.menagerie.ophelia
import android.app.Activity
import android.app.Application
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.menagerie.ophelia.database.polycule.BioListViewModel
import com.menagerie.ophelia.database.polycule.PolyculeApplication
import com.menagerie.ophelia.database.polycule.PolyculeDatabaseManager
import com.menagerie.ophelia.database.polycule.PolyculeRepository
import com.menagerie.ophelia.database.polycule.entity.Bio
import com.menagerie.ophelia.ui.theme.OpheliaTheme
import com.menagerie.ophelia.view.biographies.BioCardList
import com.menagerie.ophelia.view.PolyculeApp
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
OpheliaTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
val repository = (application as PolyculeApplication).repository
PolyculeDatabaseManager.polyculeRepository = repository
Greeting("Android")
}
val repository = (application as PolyculeApplication).repository
PolyculeDatabaseManager.polyculeRepository = repository
PolyculeApp()
}
}
}
}
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
val mBioListViewModel: BioListViewModel = viewModel(
factory = BioListViewModel.BioListViewModelFactory()
)
val items = mBioListViewModel.allBios.observeAsState(listOf()).value
BioCardList(bioList = items)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BioList(
list: List<Bio>,
mBioListViewModel: BioListViewModel
) {
Log.d("Test" ,"${list.size}")
}

View file

@ -1,6 +1,7 @@
package com.menagerie.ophelia.database.polycule
import android.content.Context
import androidx.compose.ui.tooling.preview.datasource.LoremIpsum
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
@ -14,7 +15,7 @@ import kotlinx.coroutines.launch
@Database(
version = 1,
version = 2,
exportSchema = false,
entities = [
Bio::class
@ -62,21 +63,40 @@ abstract class PolyculeDatabase : RoomDatabase() {
}
suspend fun populateDatabase(bioDao: BioDao) {
var desc = LoremIpsum(15).values.toList()
var init = ""
repeat(desc.size) {
init += ("${desc[it]} ")
}
var bio = Bio(
name = "Azea",
description = "",
description = init,
pfpRes = R.drawable.ic_app_logo
)
bioDao.insert(bio)
desc = LoremIpsum(17).values.toList()
init = ""
repeat(desc.size) {
init += ("${desc[it]} ")
}
bio = Bio(
name = "Darkwood Mill",
description = "",
description = init,
pfpRes = R.drawable.ic_app_logo
)
bioDao.insert(bio)
desc = LoremIpsum(42).values.toList()
init = ""
repeat(desc.size) {
init += ("${desc[it]} ")
}
bio = Bio(
name = "Blizzard",
description = "",
description = init,
pfpRes = R.drawable.ic_app_logo
)
bioDao.insert(bio)

View file

@ -9,21 +9,27 @@ class PolyculeRepository(
private val bioDao: BioDao
) {
suspend fun upsertBio(bio: Bio): Long {
return 0
//return bioDao.findOrInsert(bio)
suspend fun upsertBio(bio: Bio) {
return bioDao.upsert(bio)
}
fun getAlphabetisedBios(): Flow<List<Bio>> {
return bioDao.getAllAlphabetisedBios()
}
fun getBio(bioId: Int): Flow<Bio> {
return bioDao.getBio(bioId)
}
fun getAllBios(): Flow<List<Bio>> {
return bioDao.getAllBios()
}
suspend fun insert(bio : Bio) {
bioDao.insert(bio)
}
suspend fun delete(bio: Bio) {
bioDao.delete(bio)
}
}

View file

@ -3,13 +3,14 @@ package com.menagerie.ophelia.database.polycule.entity
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.menagerie.ophelia.R
@Entity(
tableName = "bio_table",
)
data class Bio(
@PrimaryKey
@ColumnInfo(name = "name") val name: String,
@ColumnInfo(name = "description") val description: String,
@ColumnInfo(name = "pfpRes") val pfpRes: Int,
@PrimaryKey(autoGenerate = true) val id: Int = 0,
@ColumnInfo(name = "name") val name: String = "",
@ColumnInfo(name = "description") val description: String = "",
@ColumnInfo(name = "pfpRes") val pfpRes: Int = R.drawable.ic_app_logo,
)

View file

@ -1,9 +1,11 @@
package com.menagerie.ophelia.database.polycule.entity
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import androidx.room.Upsert
import kotlinx.coroutines.flow.Flow
@Dao
@ -12,6 +14,9 @@ abstract class BioDao {
@Insert
abstract suspend fun insert(newBio: Bio)
@Delete
abstract suspend fun delete(bio: Bio)
@Update
abstract suspend fun update(existingBio: Bio) : Int
@ -21,4 +26,9 @@ abstract class BioDao {
@Query("SELECT * FROM bio_table")
abstract fun getAllBios(): Flow<List<Bio>>
@Query("SELECT * FROM bio_table WHERE id = :bioId")
abstract fun getBio(bioId: Int): Flow<Bio>
@Upsert
abstract suspend fun upsert(bio: Bio)
}

View file

@ -0,0 +1,33 @@
package com.menagerie.ophelia.database.polycule.entity.viewmodel
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.asLiveData
import com.menagerie.ophelia.database.polycule.PolyculeDatabaseManager
import com.menagerie.ophelia.database.polycule.PolyculeRepository
class BioDetailViewModel() : ViewModel(){
private var bioId: Int = 0
var bio = PolyculeDatabaseManager.polyculeRepository.getBio(bioId).asLiveData()
fun setBio(bioId: Int)
{
this.bioId = bioId
bio = PolyculeDatabaseManager.polyculeRepository.getBio(bioId).asLiveData()
}
class BioDetailViewModelFactory() :
ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>, ): T {
if (modelClass.isAssignableFrom(BioDetailViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return BioDetailViewModel() as T
}
throw IllegalArgumentException("Unknown ViewModel Class")
}
}
}

View file

@ -1,10 +1,11 @@
package com.menagerie.ophelia.database.polycule
package com.menagerie.ophelia.database.polycule.entity.viewmodel
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import com.menagerie.ophelia.database.polycule.PolyculeDatabaseManager
import com.menagerie.ophelia.database.polycule.entity.Bio
import kotlinx.coroutines.launch
@ -13,10 +14,14 @@ class BioListViewModel() : ViewModel() {
val allBios: LiveData<List<Bio>> = PolyculeDatabaseManager.polyculeRepository.getAllBios().asLiveData()
fun insert(bio: Bio) = viewModelScope.launch {
fun upsert(bio: Bio) = viewModelScope.launch {
PolyculeDatabaseManager.polyculeRepository.upsertBio(bio)
}
fun delete(bio: Bio) = viewModelScope.launch {
PolyculeDatabaseManager.polyculeRepository.delete(bio)
}
class BioListViewModelFactory() :
ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {

View file

@ -0,0 +1,98 @@
package com.menagerie.ophelia.view
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.viewmodel.compose.viewModel
import com.menagerie.ophelia.R
import com.menagerie.ophelia.database.polycule.entity.viewmodel.BioListViewModel
import com.menagerie.ophelia.database.polycule.entity.Bio
import com.menagerie.ophelia.view.biographies.BioCardList
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeScreen(
modifier: Modifier = Modifier,
onBioClick: (Bio) -> Unit = {},
onAddClick: () -> Unit,
) {
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
Scaffold(
modifier = modifier,
bottomBar = {
HomeTopAppBar(
onFilterClick = { },
scrollBehavior = scrollBehavior
)
}
) {
HomeBioScreen(
onBioClick = onBioClick,
onAddClick = onAddClick,
modifier = modifier.padding(it)
)
}
}
@Composable
fun HomeBioScreen(
onBioClick: (Bio) -> Unit,
onAddClick: () -> Unit = {},
modifier: Modifier,
) {
val mBioListViewModel: BioListViewModel = viewModel(
factory = BioListViewModel.BioListViewModelFactory()
)
val items = mBioListViewModel.allBios.observeAsState(listOf()).value
Column(
modifier = modifier
){
BioCardList(bioList = items, onBioClick = onBioClick)
FloatingActionButton(
onClick = onAddClick
) {
Icon(Icons.Filled.Add, "Add Bio")
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeTopAppBar(
onFilterClick: () -> Unit,
modifier: Modifier = Modifier,
scrollBehavior: TopAppBarScrollBehavior,
) {
TopAppBar(
title = {
Row(
Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
) {
}
},
modifier = modifier.statusBarsPadding(),
actions = {},
scrollBehavior = scrollBehavior
)
}

View file

@ -0,0 +1,59 @@
package com.menagerie.ophelia.view
import android.app.Activity
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.menagerie.ophelia.view.biographies.AddBiography
import com.menagerie.ophelia.view.biographies.BioDetailsScreen
@Composable
fun PolyculeApp() {
val navController = rememberNavController()
PolyculeNavHost(
navController = navController
)
}
@Composable
fun PolyculeNavHost(
navController: NavHostController
) {
val activity = (LocalContext.current as Activity)
NavHost(
navController = navController,
startDestination = "home"
) {
composable("home") {
HomeScreen(
onBioClick = {
navController.navigate("bioDetail/${it.id}")
},
onAddClick = {
navController.navigate("addBio")
}
)
}
composable(
"bioDetail/{bioId}",
arguments = listOf(navArgument("bioId") {
type = NavType.IntType
})
) {
BioDetailsScreen(
id = it.arguments?.getInt("bioId") ?: 0,
onBackClick = { navController.navigateUp() },
)
}
composable("addBio") {
AddBiography()
}
}
}

View file

@ -0,0 +1,102 @@
package com.menagerie.ophelia.view.biographies
import android.annotation.SuppressLint
import android.widget.Toast
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.unit.dp
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import com.menagerie.ophelia.R
import com.menagerie.ophelia.database.polycule.entity.viewmodel.BioListViewModel
import com.menagerie.ophelia.database.polycule.entity.Bio
import com.menagerie.ophelia.view.components.InputFieldComponent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import com.menagerie.ophelia.view.components.fab.FABComponent
fun insertBioInDB(bio: Bio?, mBioListViewModel: BioListViewModel) {
bio?.let {
mBioListViewModel.upsert(it)
}
}
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AddBiography() {
val inputViewModel = InputViewModel()
val context = LocalContext.current
val mBioListViewModel: BioListViewModel = viewModel(
factory = BioListViewModel.BioListViewModelFactory()
)
Scaffold(
floatingActionButton = {
FABComponent(text = "${R.string.add_bio}", onClick = {
insertBioInDB(inputViewModel.bio.value, mBioListViewModel)
Toast.makeText(context, "Added Bio", Toast.LENGTH_SHORT).show()
})
}
)
{
InputFieldState(inputViewModel)
}
}
@Composable
fun InputFieldState(
inputViewModel: InputViewModel,
) {
val bio: Bio by inputViewModel.bio.observeAsState(Bio())
Column(
modifier = Modifier.padding(16.dp)){
InputField(bio.name){inputViewModel.onInputChange(it)}
Spacer(modifier = Modifier.padding(10.dp))
}
}
@Composable
fun InputField(
name: String,
onValChange: ((String) -> Unit)?
) {
val focusManager = LocalFocusManager.current
if(onValChange != null) {
InputFieldComponent(
text = name,
onChange = onValChange,
label = "Enter Bio",
modifier = Modifier
.padding(all = 16.dp)
.fillMaxWidth(),
keyboardActions = KeyboardActions( onDone = {focusManager.clearFocus()})
)
}
}
class InputViewModel : ViewModel() {
private val _bio: MutableLiveData<Bio> = MutableLiveData(Bio())
val bio: LiveData<Bio> = _bio
fun onInputChange(name: String)
{
val newBio = Bio(
name = name,
)
_bio.value = newBio
}
}

View file

@ -1,34 +1,61 @@
package com.menagerie.ophelia.view.biographies
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.Card
import androidx.compose.material3.CardColors
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.menagerie.ophelia.R
import com.menagerie.ophelia.database.polycule.entity.Bio
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@Composable
fun BioCard(name: String, description: String, image: Int) {
fun BioCard(
name: String,
description: String,
image: Int,
onBioClick: () -> Unit,
onDeleteClick: () -> Unit
) {
var expanded by remember { mutableStateOf(false) }
Card(
onClick = onBioClick,
modifier = Modifier
.padding(10.dp)
.fillMaxWidth()
.wrapContentHeight(),
.wrapContentHeight()
,
shape = MaterialTheme.shapes.medium,
elevation = CardDefaults.cardElevation(
defaultElevation = 6.dp
@ -39,7 +66,7 @@ fun BioCard(name: String, description: String, image: Int) {
border = BorderStroke(1.dp, Color.Black),
) {
Row(
verticalAlignment = Alignment.CenterVertically
verticalAlignment = Alignment.CenterVertically,
) {
Image(
painter = painterResource(id = image),
@ -55,11 +82,24 @@ fun BioCard(name: String, description: String, image: Int) {
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.onSurface,
)
Text(
text = description,
style = MaterialTheme.typography.bodyMedium
)
}
Spacer(modifier = Modifier.weight(1f).fillMaxSize())
Box(
modifier = Modifier
.wrapContentSize(Alignment.TopStart)
)
{
IconButton(
onClick = { expanded = true }) {
Icon(
Icons.Default.MoreVert,
contentDescription = ""
)
}
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
DropdownMenuItem(text = { Text(text = "Delete")}, onClick = onDeleteClick)
}
}
}
}
}
}

View file

@ -10,56 +10,54 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import com.menagerie.ophelia.database.polycule.entity.Bio
import androidx.compose.ui.unit.dp
import com.menagerie.ophelia.R
import androidx.lifecycle.viewmodel.compose.viewModel
import com.menagerie.ophelia.database.polycule.entity.Bio
import com.menagerie.ophelia.database.polycule.entity.viewmodel.BioListViewModel
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BioCardList(bioList: List<Bio>) {
fun BioCardList(
bioList: List<Bio>,
onBioClick: (Bio) -> Unit = {},
) {
val mBioListViewModel: BioListViewModel = viewModel(
factory = BioListViewModel.BioListViewModelFactory()
)
Scaffold (
topBar = {
TopAppBar(
title = {Text(stringResource(id = R.string.app_name)) }
)
}
) {
innerPadding ->
Box(modifier = Modifier.padding(innerPadding)) {
LazyColumn(
modifier = Modifier.fillMaxWidth(),
contentPadding = PaddingValues(8.dp)
) {
item {
Row(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(vertical = 25.dp),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Text(
"\uD83c\uDF3f The Menagerie",
style = MaterialTheme.typography.headlineLarge
)
}
}
items(bioList) { bio ->
BioCard(bio.name, bio.description, bio.pfpRes)
Box(modifier = Modifier.padding()) {
LazyColumn(
modifier = Modifier.fillMaxWidth(),
contentPadding = PaddingValues(8.dp)
) {
item {
Row(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(vertical = 24.dp),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
Text(
"The Menagerie",
style = MaterialTheme.typography.headlineLarge
)
}
}
items(bioList) { bio ->
BioCard(
name = bio.name,
description = bio.description,
image = bio.pfpRes,
onBioClick = {onBioClick(bio)},
onDeleteClick = { mBioListViewModel.delete(bio) })
}
}
}
}

View file

@ -0,0 +1,136 @@
package com.menagerie.ophelia.view.biographies
import android.util.Log
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.menagerie.ophelia.database.polycule.entity.Bio
import com.menagerie.ophelia.database.polycule.entity.viewmodel.BioDetailViewModel
import androidx.constraintlayout.compose.ConstraintLayout
import com.menagerie.ophelia.view.components.utils.Dimens
data class BioDetailCallbacks(
val onBackClick: () -> Unit
)
@Composable
fun BioDetailsScreen(
id: Int,
onBackClick: () -> Unit,
) {
val mBioDetailViewModel: BioDetailViewModel = viewModel(
)
mBioDetailViewModel.setBio(bioId = id)
val bio = mBioDetailViewModel.bio.observeAsState().value
if(bio != null) {
Surface {
BioDetails(
bio,
BioDetailCallbacks(
onBackClick = onBackClick
)
)
}
}
}
@Composable
fun BioDetails(
bio: Bio,
callbacks: BioDetailCallbacks,
modifier: Modifier = Modifier
) {
Box(
modifier
.fillMaxSize()
) {
BioDetailContents(
bio = bio,
imageHeight = with(LocalDensity.current) {
1.dp
},
)
}
}
@Composable
fun BioDetailContents(
bio: Bio,
imageHeight: Dp,
){
Column {
ConstraintLayout {
val(image, info) = createRefs()
Image(
painter = painterResource(id = bio.pfpRes),
contentDescription = null,
modifier = Modifier
.size(130.dp)
.padding(8.dp),
contentScale = ContentScale.Fit,
)
InfoDetails(
name = bio.name,
description = bio.description,
modifier = Modifier.constrainAs(info) {
top.linkTo(image.bottom)
}
)
}
}
}
@Composable
fun InfoDetails(
name: String,
description: String,
modifier: Modifier = Modifier
) {
Column(modifier = modifier.padding(Dimens.PaddingLarge)) {
Text(
text = name,
style = MaterialTheme.typography.displaySmall,
modifier = Modifier
.padding(
start = Dimens.PaddingSmall,
end = Dimens.PaddingSmall,
bottom = Dimens.PaddingNormal
)
.align(Alignment.CenterHorizontally)
)
Box(
Modifier
.align(Alignment.CenterHorizontally)
.padding(
start = Dimens.PaddingSmall,
end = Dimens.PaddingSmall,
bottom = Dimens.PaddingNormal
)
) {
Text(text = description)
}
}
}

View file

@ -0,0 +1,28 @@
package com.menagerie.ophelia.view.components
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.modifier.modifierLocalConsumer
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun InputFieldComponent(
text: String,
onChange: (String) -> Unit,
modifier: Modifier = Modifier,
singleLine: Boolean = true,
label: String = "Some val",
keyboardActions: KeyboardActions = KeyboardActions.Default,
) {
OutlinedTextField(
text,
onChange,
label = { Text(text = label)},
modifier = modifier,
singleLine = singleLine,
)
}

View file

@ -0,0 +1,20 @@
package com.menagerie.ophelia.view.components.fab
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.FloatingActionButtonDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
@Composable
fun FABComponent(
text: String,
onClick: () -> Unit
) {
ExtendedFloatingActionButton(
onClick = onClick,
elevation = FloatingActionButtonDefaults.elevation(8.dp)
){
Text(text = text)
}
}

View file

@ -0,0 +1,25 @@
package com.menagerie.ophelia.view.components.utils
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.menagerie.ophelia.R
object Dimens {
val PaddingSmall: Dp
@Composable get() = dimensionResource(R.dimen.margin_small)
val PaddingNormal: Dp
@Composable get() = dimensionResource(R.dimen.margin_normal)
val PaddingLarge: Dp = 24.dp
val PlantDetailAppBarHeight: Dp
@Composable get() = dimensionResource(R.dimen.plant_detail_app_bar_height)
val ToolbarIconPadding = 12.dp
val ToolbarIconSize = 32.dp
}

View file

@ -0,0 +1,62 @@
package com.menagerie.ophelia.view.components.utils
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Shapes
import androidx.compose.material3.Snackbar
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.modifier.modifierLocalConsumer
import androidx.compose.ui.unit.dp
@Composable
fun TextSnackbarContainer(
snackbarText: String,
showSnackbar: Boolean,
onDismissSnackbar: () -> Unit,
modifier: Modifier = Modifier,
snackbarHostState: SnackbarHostState = remember{ SnackbarHostState()},
content: @Composable () -> Unit,
) {
Box(modifier) {
content()
val onDismissState by rememberUpdatedState(onDismissSnackbar)
LaunchedEffect(showSnackbar, snackbarText) {
if (showSnackbar) {
try {
snackbarHostState.showSnackbar(
message = snackbarText,
duration = SnackbarDuration.Short
)
} finally {
onDismissState()
}
}
}
MaterialTheme(shapes = Shapes()) {
SnackbarHost(
hostState = snackbarHostState,
modifier = modifier
.align(Alignment.BottomCenter)
.systemBarsPadding()
.padding(all = 8.dp),
) {
Snackbar(it)
}
}
}
}

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_graph">
</navigation>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Ophelia" parent="android:Theme.Material.Light.NoActionBar" />
<style name="Theme.Ophelia" parent="android:ThemeOverlay.Material.Dark.ActionBar" />
<style name="SplashScreenTheme" parent="Theme.Ophelia">
<item name="windowSplashScreenBackground">@color/purple_200</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/bg_splash_12</item>

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Per Material Design specs: -->
<!-- https://material.io/design/components/buttons-floating-action-button.html#specs -->
<dimen name="fab_margin">16dp</dimen>
<!-- Padding on the bottom of a screen so that the FAB won't overlap scrolled text. -->
<dimen name="fab_bottom_padding">72dp</dimen>
<dimen name="plant_detail_app_bar_height">278dp</dimen>
<dimen name="margin_normal">16dp</dimen>
<dimen name="margin_small">8dp</dimen>
<dimen name="margin_extra_small">4dp</dimen>
<dimen name="padding_large">48dp</dimen>
<dimen name="plant_item_image_height">95dp</dimen>
<dimen name="card_side_margin">12dp</dimen>
<dimen name="card_bottom_margin">26dp</dimen>
<dimen name="card_elevation">2dp</dimen>
<dimen name="card_corner_radius">12dp</dimen>
<!-- elevation for Plant Detail toolbar - used to convert to pixels when hiding/showing -->
<dimen name="toolbar_elevation">5dp</dimen>
<!-- Plant List Header margin -->
<dimen name="header_margin">24dp</dimen>
<dimen name="gallery_header_margin">72dp</dimen>
<!-- minimum height of plant detail page so that the collapsing toolbar can be shown on every page -->
<dimen name="plant_description_min_height">555dp</dimen>
</resources>

View file

@ -1,3 +1,7 @@
<resources>
<string name="app_name">Ophelia</string>
<string name="add_bio">Add Bio</string>
<string name="edit_bio">Edit Bio</string>
<string name="bio_load">Bio Loaded</string>
<string name="open_context_menu">Open context menu</string>
</resources>