diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000000000000000000000000000000000000..28422375b649dbd42cb2368f416511871f9fdda3 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,20 @@ +<component name="InspectionProjectProfileManager"> + <profile version="1.0"> + <option name="myName" value="Project Default" /> + <inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true"> + <option name="previewFile" value="true" /> + </inspection_tool> + <inspection_tool class="PreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true"> + <option name="previewFile" value="true" /> + </inspection_tool> + <inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true"> + <option name="previewFile" value="true" /> + </inspection_tool> + <inspection_tool class="PreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true"> + <option name="previewFile" value="true" /> + </inspection_tool> + <inspection_tool class="PreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true"> + <option name="previewFile" value="true" /> + </inspection_tool> + </profile> +</component> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 99f9a25638cac713ad668270026a540159c7175f..55bc8a9a62c562249374913545b70c0fe8442c91 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -23,6 +23,9 @@ <entry key="../../../../layout/compose-model-1638790892890.xml" value="0.1" /> <entry key="../../../../layout/compose-model-1638793251791.xml" value="0.14358108108108109" /> <entry key="../../../../layout/compose-model-1638793936652.xml" value="0.15625" /> + <entry key="../../../../layout/compose-model-1638879424123.xml" value="0.15625" /> + <entry key="../../../../layout/compose-model-1638892987664.xml" value="0.1570945945945946" /> + <entry key="../../../../layout/compose-model-1638894001064.xml" value="0.165" /> <entry key="app/src/main/res/layout/activity_create_character.xml" value="0.16510416666666666" /> <entry key="app/src/main/res/layout/activity_main.xml" value="0.16510416666666666" /> <entry key="app/src/main/res/layout/activity_profile.xml" value="0.165" /> diff --git a/app/src/main/java/com/flyinpancake/dndspells/CharacterDetailsActivity.kt b/app/src/main/java/com/flyinpancake/dndspells/CharacterDetailsActivity.kt index c6396500e5a430c47180f093db21cbc3998c9be8..f56cfff858aba2555e63ccc633542926134fa9db 100644 --- a/app/src/main/java/com/flyinpancake/dndspells/CharacterDetailsActivity.kt +++ b/app/src/main/java/com/flyinpancake/dndspells/CharacterDetailsActivity.kt @@ -5,30 +5,32 @@ import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.* import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.outlined.Add import androidx.compose.runtime.Composable -import androidx.compose.runtime.State import androidx.compose.runtime.livedata.observeAsState -import androidx.compose.runtime.mutableStateOf 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.ViewModelProvider -import androidx.lifecycle.get -import androidx.lifecycle.map import com.flyinpancake.dndspells.CharacterDetailsActivity.Companion.KEY_NAME import com.flyinpancake.dndspells.model.DndCharacter import com.flyinpancake.dndspells.model.Spell import com.flyinpancake.dndspells.ui.theme.DndSpellsTheme import com.flyinpancake.dndspells.viewmodel.CharacterViewModel - +import com.flyinpancake.dndspells.viewmodel.SpellViewModel +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import java.util.* class CharacterDetailsActivity : ComponentActivity() { @@ -40,22 +42,25 @@ class CharacterDetailsActivity : ComponentActivity() { setContent { DndSpellsTheme { // A surface container using the 'background' color from the theme - val viewModel = ViewModelProvider(this)[CharacterViewModel::class.java] + val viewModel = ViewModelProvider(this) val nameInDb = intent.getStringExtra(KEY_NAME)?:"" - val character = viewModel.get(nameInDb).observeAsState().value?: DndCharacter() + val character = viewModel[CharacterViewModel::class.java].get(nameInDb).observeAsState().value?: DndCharacter() + val spells = + viewModel[SpellViewModel::class.java].allSpells.observeAsState().value ?: listOf() Surface(color = MaterialTheme.colors.background) { - CharacterDetailContent(character) + CharacterDetailContent(character, spells) } } } } } +@OptIn(ExperimentalSerializationApi::class) @Composable -fun CharacterDetailContent(character: DndCharacter = DndCharacter()) { - val activity = (LocalContext.current as Activity) +fun CharacterDetailContent(character: DndCharacter = DndCharacter(), spells: List<Spell> = listOf()) { + val activity = (LocalContext.current as? Activity) Scaffold( topBar = { DndTopBar(character.name) }, @@ -65,7 +70,7 @@ fun CharacterDetailContent(character: DndCharacter = DndCharacter()) { onClick = { val intent = Intent(activity, SelectSpellsActivity::class.java) intent.putExtra(KEY_NAME, character.name) - activity.startActivity(intent) + activity?.startActivity(intent) }, icon = { Icon(Icons.Outlined.Add, null) @@ -73,31 +78,46 @@ fun CharacterDetailContent(character: DndCharacter = DndCharacter()) { ) }, ) { - if (character.spellList != null) { - - LazyColumn() { - items(items = character.spellList!!.toList(), itemContent = { - SpellListItem(it) - }) - } - } + CharacterSpellList(spells = spells, character = character, onItemClick = { spell -> + val intent = Intent(activity, SpellDetailsActivity::class.java) + intent.putExtra(SpellDetailsActivity.KEY_SPELLNAME, Json.encodeToString(spell)) + activity?.startActivity(intent) + }) } } +@OptIn(ExperimentalMaterialApi::class) @Composable -fun SpellListItem(spell: Spell) { +fun SpellListItem(spell: Spell, onItemClick: (Spell) -> Unit = {}) { Card( - modifier = Modifier.padding(start = 25.dp, end = 25.dp, bottom = 10.dp) + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 25.dp, vertical = 5.dp), + onClick = { onItemClick(spell) } ) { - Text(spell.name) + Row( + Modifier + .fillMaxSize() + .padding(10.dp), + ) { + SpellContent(spell = spell) + } + } +} + +@Composable +fun CharacterSpellList(character: DndCharacter, spells: List<Spell>, onItemClick: (Spell) -> Unit = {} ) { + LazyColumn() { + items(spells.filter { character.spellList.contains(it.name) }) { + SpellListItem(it, onItemClick) + } } } @Preview(showBackground = true) @Composable fun CharacterDetailPreview() { - MyApp { - - CharacterDetailContent() + DndSpellsTheme { + CharacterDetailContent(sampleCharacter, samplespells) } } \ No newline at end of file diff --git a/app/src/main/java/com/flyinpancake/dndspells/CreateCharacterActivity.kt b/app/src/main/java/com/flyinpancake/dndspells/CreateCharacterActivity.kt index d1fe66a67db37d5e28b5d26be1a73d116d0f0416..183d9c943c71eeff5606d1612276084687ff77c6 100644 --- a/app/src/main/java/com/flyinpancake/dndspells/CreateCharacterActivity.kt +++ b/app/src/main/java/com/flyinpancake/dndspells/CreateCharacterActivity.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Add +import androidx.compose.material.icons.outlined.ArrowBack import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -16,6 +17,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.ViewModelProvider +import androidx.navigation.findNavController import com.flyinpancake.dndspells.model.DndCharacter import com.flyinpancake.dndspells.ui.theme.DndSpellsTheme import com.flyinpancake.dndspells.viewmodel.CharacterViewModel @@ -296,6 +298,8 @@ fun Title( @Composable fun DndTopBar(titleText: String? = null) { + val activity = (LocalContext.current as? Activity) + TopAppBar( title = { Text( @@ -303,7 +307,15 @@ fun DndTopBar(titleText: String? = null) { ) }, elevation = 0.dp, - backgroundColor = MaterialTheme.colors.primary + backgroundColor = MaterialTheme.colors.primary, + navigationIcon = { + IconButton(onClick = { activity?.onBackPressed() }) { + Icon( + Icons.Outlined.ArrowBack, + "" + ) + } + } ) } diff --git a/app/src/main/java/com/flyinpancake/dndspells/SelectSpellsActivity.kt b/app/src/main/java/com/flyinpancake/dndspells/SelectSpellsActivity.kt index f79df12dbc50ee8f9dceb0c66071a7a85d8c3e0d..1175c23b17284baa1c9d09af2d616ffc1c23ec7a 100644 --- a/app/src/main/java/com/flyinpancake/dndspells/SelectSpellsActivity.kt +++ b/app/src/main/java/com/flyinpancake/dndspells/SelectSpellsActivity.kt @@ -57,16 +57,16 @@ fun SelectSpellsContent( items( items = spells.toList(), itemContent = { spell -> - var selectedState by remember { mutableStateOf( character.spellList?.contains(spell) ?: false)} + var selectedState by remember { mutableStateOf( character.spellList.contains(spell.name)) } SpellCardWithCheckBox( spell = spell, checked = selectedState, onCheck = { newContains -> - val newSpellList = character.spellList?.toMutableList()?: mutableListOf() - if (newContains) { - newSpellList.add(spell) - } else { - newSpellList.remove(spell) + val newSpellList = character.spellList.toMutableList() + if (newContains && !newSpellList.contains(spell.name)) { + newSpellList.add(spell.name) + } else if (newSpellList.contains(spell.name)){ + newSpellList.remove(spell.name) } selectedState = !selectedState character.spellList = newSpellList @@ -107,11 +107,11 @@ private fun SpellCardWithCheckBox( } @Composable -private fun SpellContent(spell: Spell) { +fun SpellContent(spell: Spell) { val textPadding = Modifier.padding(horizontal = 5.dp) Column( Modifier.fillMaxSize(), - verticalArrangement = Arrangement.Center + verticalArrangement = Arrangement.Center, ) { Row( Modifier.fillMaxSize(), @@ -153,30 +153,33 @@ private fun SelectableSpellList(spells: List<Spell>, character: DndCharacter) { } +val samplespells = listOf( + Spell( + name = "Power Word Kill", + level = 9, + classes = "Wizard", + components = "V", + desc = "Kil", + duration = "1 action", + range = "240 ft", + ritual = false, + school = "E", + time = "instantanius" + ) +) + +val sampleCharacter = DndCharacter( + name = "Ba'luk", + level = 4, + dndClass = DndCharacter.DndClass.Druid, + spellList = listOf("Power Word Kill") +) + @Preview(showBackground = true) @Composable fun DefaultPreview() { - val samplespells = listOf( - Spell( - name = "Power Word Kill", - level = 9, - classes = "Wizard", - components = "V", - desc = "Kil", - duration = "1 action", - range = "240 ft", - ritual = false, - school = "E", - time = "instantanius" - ) - ) - val sampleCharacter = DndCharacter( - name = "Ba'luk", - level = 4, - dndClass = DndCharacter.DndClass.Druid - ) DndSpellsTheme { SelectSpellsContent(spells = samplespells, character = sampleCharacter) diff --git a/app/src/main/java/com/flyinpancake/dndspells/database/CharacterDao.kt b/app/src/main/java/com/flyinpancake/dndspells/database/CharacterDao.kt index 5c93c39514e53f0e64bafebdd57449e9bffa50ae..0d78df4726409775c9c92400e699a9173bd99e2b 100644 --- a/app/src/main/java/com/flyinpancake/dndspells/database/CharacterDao.kt +++ b/app/src/main/java/com/flyinpancake/dndspells/database/CharacterDao.kt @@ -18,7 +18,7 @@ interface CharacterDao { fun getCharacterLiveDataByName(name: String): LiveData<RoomCharacter> @Update - fun updateCharacter(roomCharacter: RoomCharacter): Int + fun updateCharacter(vararg roomCharacters: RoomCharacter): Int @Delete fun deleteCharacter( diff --git a/app/src/main/java/com/flyinpancake/dndspells/model/DndCharacter.kt b/app/src/main/java/com/flyinpancake/dndspells/model/DndCharacter.kt index 9f9ed23ab4fafa9ae74523f71e68b7d0882657dc..05f6a41903dc051218067d3f8d82257136374834 100644 --- a/app/src/main/java/com/flyinpancake/dndspells/model/DndCharacter.kt +++ b/app/src/main/java/com/flyinpancake/dndspells/model/DndCharacter.kt @@ -1,15 +1,16 @@ package com.flyinpancake.dndspells.model data class DndCharacter( + var id: Int = 0, var name: String, var level: Int, var dndClass: DndClass, - var spellList: List<Spell>?, + var spellList: List<String>, ) { - constructor() :this(name = "" , level = 0, dndClass = DndClass.None, spellList = null) - constructor(name: String, level: Int, dndClass: DndClass) :this(name = name, level = level, dndClass = dndClass, spellList = null) + constructor() :this(name = "" , level = 0, dndClass = DndClass.None, spellList = listOf()) + constructor(name: String, level: Int, dndClass: DndClass) :this(name = name, level = level, dndClass = dndClass, spellList = listOf()) enum class DndClass(val legibleName: String) { diff --git a/app/src/main/java/com/flyinpancake/dndspells/repository/CharacterRepository.kt b/app/src/main/java/com/flyinpancake/dndspells/repository/CharacterRepository.kt index 48c7049d9841326eb09cc3670fa9b6a49073238b..34ba87746f9b6b623b6ce6d05eb01e416b178403 100644 --- a/app/src/main/java/com/flyinpancake/dndspells/repository/CharacterRepository.kt +++ b/app/src/main/java/com/flyinpancake/dndspells/repository/CharacterRepository.kt @@ -40,15 +40,16 @@ class CharacterRepository( private fun DndCharacter.toRoomDomain(): RoomCharacter { var spellNameList = "" - spellList?.forEach { + spellList.forEach { spellNameList += "\n" - spellNameList += it.name + spellNameList += it } return RoomCharacter( name = name, level = level, dndClass = dndClass.legibleName, - spellNameList = spellNameList + spellNameList = spellNameList, + id = id ) } @@ -57,10 +58,9 @@ private fun RoomCharacter.toDomainModel(): DndCharacter { val dndCharacter = DndCharacter() dndCharacter.name = name dndCharacter.level = level + dndCharacter.id = id if (spellNameList.isNotEmpty()) - dndCharacter.spellList = spellNameList.split('\n').map { spellName -> - DndApplication.spellDatabase.spellDao().getSpellByName(spellName)?.toDomainModel()!! - } + dndCharacter.spellList = spellNameList.split('\n') DndCharacter.DndClass.values().forEach { dndClass -> if (dndClass.legibleName == this.dndClass) dndCharacter.dndClass = dndClass