azea/AddEditBio #4
22 changed files with 765 additions and 116 deletions
|
@ -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"
|
||||
|
|
|
@ -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}")
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
|
@ -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,
|
||||
)
|
|
@ -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)
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 {
|
98
app/src/main/java/com/menagerie/ophelia/view/HomeScreen.kt
Normal file
98
app/src/main/java/com/menagerie/ophelia/view/HomeScreen.kt
Normal 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
|
||||
)
|
||||
}
|
||||
|
59
app/src/main/java/com/menagerie/ophelia/view/PolyculeApp.kt
Normal file
59
app/src/main/java/com/menagerie/ophelia/view/PolyculeApp.kt
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
6
app/src/main/res/navigation/nav_graph.xml
Normal file
6
app/src/main/res/navigation/nav_graph.xml
Normal 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>
|
|
@ -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>
|
||||
|
|
37
app/src/main/res/values/dimens.xml
Normal file
37
app/src/main/res/values/dimens.xml
Normal 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>
|
|
@ -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>
|
Loading…
Add table
Add a link
Reference in a new issue