diff --git a/app/build.gradle b/app/build.gradle index 90626cb..c145b89 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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" diff --git a/app/src/main/java/com/menagerie/ophelia/MainActivity.kt b/app/src/main/java/com/menagerie/ophelia/MainActivity.kt index db30ac0..0eb5196 100644 --- a/app/src/main/java/com/menagerie/ophelia/MainActivity.kt +++ b/app/src/main/java/com/menagerie/ophelia/MainActivity.kt @@ -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, - mBioListViewModel: BioListViewModel -) { - Log.d("Test" ,"${list.size}") - -} \ No newline at end of file diff --git a/app/src/main/java/com/menagerie/ophelia/database/polycule/PolyculeDatabase.kt b/app/src/main/java/com/menagerie/ophelia/database/polycule/PolyculeDatabase.kt index c10f261..97ba878 100644 --- a/app/src/main/java/com/menagerie/ophelia/database/polycule/PolyculeDatabase.kt +++ b/app/src/main/java/com/menagerie/ophelia/database/polycule/PolyculeDatabase.kt @@ -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) diff --git a/app/src/main/java/com/menagerie/ophelia/database/polycule/PolyculeRepository.kt b/app/src/main/java/com/menagerie/ophelia/database/polycule/PolyculeRepository.kt index db94018..9982108 100644 --- a/app/src/main/java/com/menagerie/ophelia/database/polycule/PolyculeRepository.kt +++ b/app/src/main/java/com/menagerie/ophelia/database/polycule/PolyculeRepository.kt @@ -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> { return bioDao.getAllAlphabetisedBios() } + fun getBio(bioId: Int): Flow { + return bioDao.getBio(bioId) + } + fun getAllBios(): Flow> { return bioDao.getAllBios() } - suspend fun insert(bio : Bio) { bioDao.insert(bio) } + suspend fun delete(bio: Bio) { + bioDao.delete(bio) + } + } \ No newline at end of file diff --git a/app/src/main/java/com/menagerie/ophelia/database/polycule/entity/Bio.kt b/app/src/main/java/com/menagerie/ophelia/database/polycule/entity/Bio.kt index 4332937..8c8e0ee 100644 --- a/app/src/main/java/com/menagerie/ophelia/database/polycule/entity/Bio.kt +++ b/app/src/main/java/com/menagerie/ophelia/database/polycule/entity/Bio.kt @@ -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, ) \ No newline at end of file diff --git a/app/src/main/java/com/menagerie/ophelia/database/polycule/entity/BioDao.kt b/app/src/main/java/com/menagerie/ophelia/database/polycule/entity/BioDao.kt index c92bbfa..d17577c 100644 --- a/app/src/main/java/com/menagerie/ophelia/database/polycule/entity/BioDao.kt +++ b/app/src/main/java/com/menagerie/ophelia/database/polycule/entity/BioDao.kt @@ -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> + @Query("SELECT * FROM bio_table WHERE id = :bioId") + abstract fun getBio(bioId: Int): Flow + + @Upsert + abstract suspend fun upsert(bio: Bio) } \ No newline at end of file diff --git a/app/src/main/java/com/menagerie/ophelia/database/polycule/entity/viewmodel/BioDetailViewModel.kt b/app/src/main/java/com/menagerie/ophelia/database/polycule/entity/viewmodel/BioDetailViewModel.kt new file mode 100644 index 0000000..974e741 --- /dev/null +++ b/app/src/main/java/com/menagerie/ophelia/database/polycule/entity/viewmodel/BioDetailViewModel.kt @@ -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 create(modelClass: Class, ): T { + if (modelClass.isAssignableFrom(BioDetailViewModel::class.java)) { + @Suppress("UNCHECKED_CAST") + return BioDetailViewModel() as T + } + throw IllegalArgumentException("Unknown ViewModel Class") + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/menagerie/ophelia/database/polycule/BioListViewModel.kt b/app/src/main/java/com/menagerie/ophelia/database/polycule/entity/viewmodel/BioListViewModel.kt similarity index 75% rename from app/src/main/java/com/menagerie/ophelia/database/polycule/BioListViewModel.kt rename to app/src/main/java/com/menagerie/ophelia/database/polycule/entity/viewmodel/BioListViewModel.kt index b555993..09243f0 100644 --- a/app/src/main/java/com/menagerie/ophelia/database/polycule/BioListViewModel.kt +++ b/app/src/main/java/com/menagerie/ophelia/database/polycule/entity/viewmodel/BioListViewModel.kt @@ -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> = 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 create(modelClass: Class): T { diff --git a/app/src/main/java/com/menagerie/ophelia/view/HomeScreen.kt b/app/src/main/java/com/menagerie/ophelia/view/HomeScreen.kt new file mode 100644 index 0000000..4d756b0 --- /dev/null +++ b/app/src/main/java/com/menagerie/ophelia/view/HomeScreen.kt @@ -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 + ) +} + diff --git a/app/src/main/java/com/menagerie/ophelia/view/PolyculeApp.kt b/app/src/main/java/com/menagerie/ophelia/view/PolyculeApp.kt new file mode 100644 index 0000000..fe7ef33 --- /dev/null +++ b/app/src/main/java/com/menagerie/ophelia/view/PolyculeApp.kt @@ -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() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/menagerie/ophelia/view/biographies/AddEditBiography.kt b/app/src/main/java/com/menagerie/ophelia/view/biographies/AddEditBiography.kt new file mode 100644 index 0000000..fa4c067 --- /dev/null +++ b/app/src/main/java/com/menagerie/ophelia/view/biographies/AddEditBiography.kt @@ -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 = MutableLiveData(Bio()) + val bio: LiveData = _bio + + fun onInputChange(name: String) + { + val newBio = Bio( + name = name, + ) + _bio.value = newBio + } +} diff --git a/app/src/main/java/com/menagerie/ophelia/view/biographies/BioCard.kt b/app/src/main/java/com/menagerie/ophelia/view/biographies/BioCard.kt index 64a2b69..38f1cee 100644 --- a/app/src/main/java/com/menagerie/ophelia/view/biographies/BioCard.kt +++ b/app/src/main/java/com/menagerie/ophelia/view/biographies/BioCard.kt @@ -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) + } } } - } + } } \ No newline at end of file diff --git a/app/src/main/java/com/menagerie/ophelia/view/biographies/BioCardList.kt b/app/src/main/java/com/menagerie/ophelia/view/biographies/BioCardList.kt index 1dea699..3d1d4d3 100644 --- a/app/src/main/java/com/menagerie/ophelia/view/biographies/BioCardList.kt +++ b/app/src/main/java/com/menagerie/ophelia/view/biographies/BioCardList.kt @@ -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) { +fun BioCardList( + bioList: List, + 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) }) + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/menagerie/ophelia/view/biographies/BioDetailsScreen.kt b/app/src/main/java/com/menagerie/ophelia/view/biographies/BioDetailsScreen.kt new file mode 100644 index 0000000..22ed243 --- /dev/null +++ b/app/src/main/java/com/menagerie/ophelia/view/biographies/BioDetailsScreen.kt @@ -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) + } + } +} diff --git a/app/src/main/java/com/menagerie/ophelia/view/components/InputFieldComponent.kt b/app/src/main/java/com/menagerie/ophelia/view/components/InputFieldComponent.kt new file mode 100644 index 0000000..36c94d6 --- /dev/null +++ b/app/src/main/java/com/menagerie/ophelia/view/components/InputFieldComponent.kt @@ -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, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/menagerie/ophelia/view/components/fab/FABComponent.kt b/app/src/main/java/com/menagerie/ophelia/view/components/fab/FABComponent.kt new file mode 100644 index 0000000..f838e6e --- /dev/null +++ b/app/src/main/java/com/menagerie/ophelia/view/components/fab/FABComponent.kt @@ -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) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/menagerie/ophelia/view/components/utils/Dimens.kt b/app/src/main/java/com/menagerie/ophelia/view/components/utils/Dimens.kt new file mode 100644 index 0000000..ecf7620 --- /dev/null +++ b/app/src/main/java/com/menagerie/ophelia/view/components/utils/Dimens.kt @@ -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 +} \ No newline at end of file diff --git a/app/src/main/java/com/menagerie/ophelia/view/components/utils/TextSnackbarContainer.kt b/app/src/main/java/com/menagerie/ophelia/view/components/utils/TextSnackbarContainer.kt new file mode 100644 index 0000000..e0813d2 --- /dev/null +++ b/app/src/main/java/com/menagerie/ophelia/view/components/utils/TextSnackbarContainer.kt @@ -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) + } + + } + } +} diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml new file mode 100644 index 0000000..8e0008b --- /dev/null +++ b/app/src/main/res/navigation/nav_graph.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values-v31/themes.xml b/app/src/main/res/values-v31/themes.xml index 6f4f49c..120cbe6 100644 --- a/app/src/main/res/values-v31/themes.xml +++ b/app/src/main/res/values-v31/themes.xml @@ -1,6 +1,6 @@ -