лаба 4 (что-то не так с навигацией)

This commit is contained in:
2023-12-20 21:45:22 +04:00
parent ac4ae739aa
commit 6fa8427b2d
33 changed files with 1100 additions and 290 deletions

View File

@@ -51,6 +51,10 @@ android {
}
dependencies {
implementation("androidx.datastore:datastore-preferences:1.0.0")
implementation("io.github.vanpra.compose-material-dialogs:datetime:0.8.1-rc")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.6")
implementation("com.jakewharton.threetenabp:threetenabp:1.2.1")
implementation("androidx.paging:paging-compose:3.2.1")
val room_version = "2.6.1"

View File

@@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">
<application
android:name=".GenshinApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"

View File

@@ -16,6 +16,7 @@ import com.example.android_genshin.ui.theme.Android_GenshinTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
application.deleteDatabase("genshin_db")
setContent {
Android_GenshinTheme {
// A surface container using the 'background' color from the theme

View File

@@ -16,7 +16,10 @@ enum class Screen(
) {
CharacterList("character_list", R.string.character_main_title, Icons.Filled.List),
About("about", R.string.about_title, Icons.Filled.Info),
CharacterView("character-view/{id}", R.string.character_view_title, showInBottomBar = false),
CharacterView("character_view/{id}", R.string.character_view_title, showInBottomBar = false),
CharacterEdit("character_edit/{id}", R.string.character_view_title, showInBottomBar = false),
RarityEdit("rarity_edit/{id}", R.string.rarity_view_title, showInBottomBar = false),
WeaponEdit("weapon_edit/{id}", R.string.weapon_view_title, showInBottomBar = false),
RarityList("rarity_list", R.string.rarity_main_title, Icons.Filled.List),
VisionList("vision_list", R.string.vision_main_title, Icons.Filled.List),
WeaponList("weapon_list", R.string.weapon_main_title, Icons.Filled.List),

View File

@@ -83,9 +83,9 @@ abstract class AppDatabase : RoomDatabase() {
visionDao.insert(vision3)
// Weapons
val weaponDao = database.weaponDao()
val weapon1 = Weapon(1, "Weapon 1", "Spear", 545)
val weapon2 = Weapon(2, "Weapon 2", "Sword", 455)
val weapon3 = Weapon(3, "Weapon 3", "Claymore", 582)
val weapon1 = Weapon(1, "Weapon 1", "Spear", 545, createColoredImage(Color.CYAN))
val weapon2 = Weapon(2, "Weapon 2", "Sword", 455, createColoredImage(Color.YELLOW))
val weapon3 = Weapon(3, "Weapon 3", "Claymore", 582, createColoredImage(Color.RED))
weaponDao.insert(weapon1)
weaponDao.insert(weapon2)
weaponDao.insert(weapon3)

View File

@@ -1,6 +1,7 @@
package com.example.android_genshin.database.entities.composeui
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.createSavedStateHandle
import androidx.lifecycle.viewmodel.CreationExtras
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
@@ -12,28 +13,34 @@ object AppViewModelProvider {
RarityListViewModel(genshinApplication().container.rarityRepository)
}
initializer {
RarityEditViewModel()
}
initializer {
RarityViewModel()
RarityEditViewModel(this.createSavedStateHandle(),
genshinApplication().container.rarityRepository)
}
//initializer {
// RarityViewModel(this.createSavedStateHandle(),
// genshinApplication().container.rarityRepository)
//}
initializer {
VisionListViewModel(genshinApplication().container.visionRepository)
}
initializer {
VisionEditViewModel()
}
initializer {
VisionViewModel()
}
//initializer {
// VisionEditViewModel(this.createSavedStateHandle(),
// genshinApplication().container.visionRepository)
//}
//initializer {
// VisionViewModel(this.createSavedStateHandle(),
// genshinApplication().container.visionRepository)
//}
initializer {
WeaponListViewModel(genshinApplication().container.weaponRepository)
}
initializer {
WeaponEditViewModel()
WeaponEditViewModel(this.createSavedStateHandle(),
genshinApplication().container.weaponRepository)
}
initializer {
WeaponViewModel()
WeaponViewModel(this.createSavedStateHandle(),
genshinApplication().container.weaponRepository)
}
initializer {
CharacterListViewModel(
@@ -45,8 +52,11 @@ object AppViewModelProvider {
)
}
initializer {
CharacterViewModel(
genshinApplication().container.characterRepository
CharacterViewModel( this.createSavedStateHandle(),
genshinApplication().container.characterRepository,
genshinApplication().container.characterRarityRepository,
genshinApplication().container.characterVisionRepository,
genshinApplication().container.characterWeaponRepository
)
}
}

View File

@@ -1,4 +1,273 @@
package com.example.android_genshin.database.entities.composeui
class CharacterEdit {
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults.TrailingIcon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.android_genshin.R
import com.example.android_genshin.database.entities.model.Rarity
import com.example.android_genshin.database.entities.model.Vision
import com.example.android_genshin.database.entities.model.Weapon
import kotlinx.coroutines.launch
@Composable
fun CharacterEdit(
navController: NavController,
viewModel: CharacterEditViewModel = viewModel(factory = AppViewModelProvider.Factory),
rarityViewModel: RarityDropDownViewModel = viewModel(factory = AppViewModelProvider.Factory),
visionViewModel: VisionDropDownViewModel = viewModel(factory = AppViewModelProvider.Factory),
weaponViewModel: WeaponDropDownViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val coroutineScope = rememberCoroutineScope()
CharacterEdit(
characterUiState = viewModel.characterUiState,
rarityUiState = rarityViewModel.rarityUiState,
visionUiState = visionViewModel.visionUiState,
weaponUiState = weaponViewModel.weaponUiState,
raritiesListUiState = rarityViewModel.raritiesListUiState,
visionsListUiState = visionViewModel.visionsListUiState,
weaponsListUiState = weaponViewModel.weaponsListUiState,
onClick = {
coroutineScope.launch {
viewModel.saveCharacter()
navController.popBackStack()
}
},
onUpdate = viewModel::updateUiState,
onRarityUpdate = viewModel::updateUiStateRarity,
onVisionUpdate = viewModel::updateUiStateVision,
onWeaponUpdate = viewModel::updateUiStateWeapon
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun RarityDropDown(
rarityUiState: RarityUiState,
raritiesListUiState: RaritiesListUiState,
onRarityUpdate: (Rarity) -> Unit
) {
var expanded: Boolean by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
modifier = Modifier
.padding(top = 7.dp),
expanded = expanded,
onExpandedChange = {
expanded = !expanded
}
) {
TextField(
value = rarityUiState.rarity?.rarityName
?: stringResource(id = R.string.character_rarity_not_select),
onValueChange = {},
readOnly = true,
trailingIcon = {
TrailingIcon(expanded = expanded)
},
modifier = Modifier
.fillMaxWidth()
.menuAnchor()
)
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier
.background(Color.White)
.exposedDropdownSize()
) {
raritiesListUiState.rarityList.forEach { rarity ->
DropdownMenuItem(
text = {
Text(text = rarity.rarityName)
},
onClick = {
onRarityUpdate(rarity)
expanded = false
}
)
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun VisionDropDown(
visionUiState: VisionUiState,
visionsListUiState: VisionsListUiState,
onVisionUpdate: (Vision) -> Unit
) {
var expanded: Boolean by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
modifier = Modifier
.padding(top = 7.dp),
expanded = expanded,
onExpandedChange = {
expanded = !expanded
}
) {
TextField(
value = visionUiState.vision?.visionName
?: stringResource(id = R.string.character_vision_not_select),
onValueChange = {},
readOnly = true,
trailingIcon = {
TrailingIcon(expanded = expanded)
},
modifier = Modifier
.fillMaxWidth()
.menuAnchor()
)
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier
.background(Color.White)
.exposedDropdownSize()
) {
visionsListUiState.visionList.forEach { vision ->
DropdownMenuItem(
text = {
Text(text = vision.visionName)
},
onClick = {
onVisionUpdate(vision)
expanded = false
}
)
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun WeaponDropDown(
weaponUiState: WeaponUiState,
weaponsListUiState: WeaponsListUiState,
onWeaponUpdate: (Weapon) -> Unit
) {
var expanded: Boolean by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
modifier = Modifier
.padding(top = 7.dp),
expanded = expanded,
onExpandedChange = {
expanded = !expanded
}
) {
TextField(
value = weaponUiState.weapon?.weaponName
?: stringResource(id = R.string.character_weapon_not_select),
onValueChange = {},
readOnly = true,
trailingIcon = {
TrailingIcon(expanded = expanded)
},
modifier = Modifier
.fillMaxWidth()
.menuAnchor()
)
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier
.background(Color.White)
.exposedDropdownSize()
) {
weaponsListUiState.weaponList.forEach { weapon ->
DropdownMenuItem(
text = {
Text(text = weapon.weaponName)
},
onClick = {
onWeaponUpdate(weapon)
expanded = false
}
)
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun CharacterEdit(
characterUiState: CharacterUiStateEdit,
rarityUiState: RarityUiState,
raritiesListUiState: RaritiesListUiState,
visionUiState: VisionUiState,
visionsListUiState: VisionsListUiState,
weaponUiState: WeaponUiState,
weaponsListUiState: WeaponsListUiState,
onClick: () -> Unit,
onUpdate: (CharacterDetails) -> Unit,
onRarityUpdate: (CharacterRarityDetails) -> Unit,
onVisionUpdate: (CharacterVisionDetails) -> Unit,
onWeaponUpdate: (CharacterWeaponDetails) -> Unit
) {
Column(
Modifier
.fillMaxWidth()
.padding(all = 10.dp)
) {
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = characterUiState.characterDetails.characterName,
onValueChange = { onUpdate(characterUiState.characterDetails.copy(characterName = it)) },
label = { Text(stringResource(id = R.string.character_characterName)) },
singleLine = true
)
RarityDropDown(
rarityUiState = rarityUiState,
raritiesListUiState = raritiesListUiState,
onRarityUpdate = {
onRarityUpdate(characterUiState.characterRarityDetails.copy(rarityId = it.rarityId))
}
)
VisionDropDown(
visionUiState = visionUiState,
visionsListUiState = visionsListUiState,
onVisionUpdate = {
onVisionUpdate(characterUiState.characterVisionDetails.copy(visionId = it.visionId))
}
)
WeaponDropDown(
weaponUiState = weaponUiState,
weaponsListUiState = weaponsListUiState,
onWeaponUpdate = {
onWeaponUpdate(characterUiState.characterWeaponDetails.copy(weaponId = it.weaponId))
}
)
Button(
onClick = onClick,
enabled = characterUiState.isEntryValid,
shape = MaterialTheme.shapes.small,
modifier = Modifier.fillMaxWidth()
) {
Text(text = stringResource(R.string.character_save_button))
}
}
}

View File

@@ -10,15 +10,10 @@ import com.example.android_genshin.database.entities.model.Character
import com.example.android_genshin.database.entities.model.CharacterRarityCrossRef
import com.example.android_genshin.database.entities.model.CharacterVisionCrossRef
import com.example.android_genshin.database.entities.model.CharacterWeaponCrossRef
import com.example.android_genshin.database.entities.model.CharacterWithRarities
import com.example.android_genshin.database.entities.model.Rarity
import com.example.android_genshin.database.entities.model.Vision
import com.example.android_genshin.database.entities.model.Weapon
import com.example.android_genshin.database.entities.repository.CharacterRarityRepository
import com.example.android_genshin.database.entities.repository.CharacterRepository
import com.example.android_genshin.database.entities.repository.CharacterVisionRepository
import com.example.android_genshin.database.entities.repository.CharacterWeaponRepository
import com.example.android_genshin.database.entities.repository.UserRepository
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
@@ -29,24 +24,15 @@ class CharacterEditViewModel(
private val characterRarityRepository: CharacterRarityRepository,
private val characterVisionRepository: CharacterVisionRepository,
private val characterWeaponRepository: CharacterWeaponRepository,
) : ViewModel() {
var characterUiState by mutableStateOf(CharacterUiStateEdit())
private set
var characterRarityUiState by mutableStateOf(CharacterRarityUiState())
private set
var characterVisionUiState by mutableStateOf(CharacterVisionUiState())
private set
var characterWeaponUiState by mutableStateOf(CharacterWeaponUiState())
private set
private val characterUid: Int = checkNotNull(savedStateHandle["characterId"])
private val characterUid: Int = checkNotNull(savedStateHandle["id"])
private val rarityUid: Int = checkNotNull(savedStateHandle["rarityId"])
private val visionUid: Int = checkNotNull(savedStateHandle["visionId"])
private val weaponUid: Int = checkNotNull(savedStateHandle["weaponId"])
init {
viewModelScope.launch {
if(characterUid > 0){
@@ -54,18 +40,6 @@ class CharacterEditViewModel(
.filterNotNull()
.first()
.toUiState(true)
characterRarityUiState = characterRarityRepository.getRaritiesFromCharacter(characterUid)
.filterNotNull()
.first()
.toUiState()
characterVisionUiState = characterVisionRepository.getVisionsFromCharacter(characterUid)
.filterNotNull()
.first()
.toUiState()
characterWeaponUiState = characterWeaponRepository.getWeaponsFromCharacter(characterUid)
.filterNotNull()
.first()
.toUiState()
}
}
}
@@ -77,24 +51,48 @@ class CharacterEditViewModel(
)
}
fun updateUiStateRarity(characterRarityDetails: CharacterRarityDetails) {
characterUiState = CharacterUiStateEdit(
characterRarityDetails = characterRarityDetails
)
}
fun updateUiStateVision(characterVisionDetails: CharacterVisionDetails) {
characterUiState = CharacterUiStateEdit(
characterVisionDetails = characterVisionDetails
)
}
fun updateUiStateWeapon(characterWeaponDetails: CharacterWeaponDetails) {
characterUiState = CharacterUiStateEdit(
characterWeaponDetails = characterWeaponDetails
)
}
suspend fun saveCharacter() {
if (validateInput()) {
if (rarityUid > 0) {
if (characterUid > 0) {
characterRarityRepository.updateCharacterRarity(CharacterRarityUiState.characterRarityDetails.toCharacterRarity(characterUid, rarityUid))
characterRepository.updateCharacter(CharacterUiStateEdit..toCharacter(characterUid))
if(characterUid > 0) {
characterRepository.updateCharacter(characterUiState.characterDetails.toCharacter(characterUid))
if (rarityUid > 0) {
characterRarityRepository.updateCharacterRarity(characterUiState.characterRarityDetails.toCharacterRarity(characterUid, rarityUid ))
}
}
if (visionUid > 0) {
characterVisionRepository.updateCharacterVision(CharacterVisionUiState.characterVisionDetails.toCharacterVision(characterUid))
characterRepository.updateCharacter(CharacterUiStateEdit.characterDetails.toCharacter(characterUid))
}
if (weaponUid > 0) {
characterWeaponRepository.updateCharacterWeapon(CharacterUiStateEdit.characterDetails.toCharacter(characterUid))
characterRepository.updateCharacter(CharacterUiStateEdit.characterDetails.toCharacter(characterUid))
}
if (rarityUid < 0 && visionUid < 0 && weaponUid < 0) {
characterRepository.insertCharacter(CharacterUiStateEdit.characterDetails.toCharacter(characterUid))
else {
characterRarityRepository.insertCharacterRarity(characterUiState.characterRarityDetails.toCharacterRarity(characterUid, rarityUid))
}
if (visionUid > 0) {
characterVisionRepository.updateCharacterVision(characterUiState.characterVisionDetails.toCharacterVision(characterUid, visionUid))
}
else {
characterVisionRepository.insertCharacterVision(characterUiState.characterVisionDetails.toCharacterVision(characterUid, visionUid))
}
if (weaponUid > 0) {
characterWeaponRepository.updateCharacterWeapon(characterUiState.characterWeaponDetails.toCharacterWeapon(characterUid, weaponUid))
}
else {
characterWeaponRepository.insertCharacterWeapon(characterUiState.characterWeaponDetails.toCharacterWeapon(characterUid, weaponUid))
}
} else {
characterRepository.insertCharacter(characterUiState.characterDetails.toCharacter())
}
}
}
@@ -108,12 +106,36 @@ class CharacterEditViewModel(
data class CharacterUiStateEdit(
val characterDetails: CharacterDetails = CharacterDetails(),
val characterRarityDetails: CharacterRarityDetails = CharacterRarityDetails(),
val characterVisionDetails: CharacterVisionDetails = CharacterVisionDetails(),
val characterWeaponDetails: CharacterWeaponDetails = CharacterWeaponDetails(),
val isEntryValid: Boolean = false
)
data class CharacterDetails(
val characterId: Int = 0,
val characterName: String = ""
val characterName: String = "",
val image: ByteArray? = null,
val userUid: Int? = 0
)
fun CharacterDetails.toCharacter(uid: Int = 0): Character = Character(
characterId = uid,
characterName = characterName,
userId = userUid,
image = image
)
fun Character.toDetails(): CharacterDetails = CharacterDetails(
characterId = characterId,
characterName = characterName,
image = image,
userUid = userId
)
fun Character.toUiState(isEntryValid: Boolean = false): CharacterUiStateEdit = CharacterUiStateEdit(
characterDetails = this.toDetails(),
isEntryValid = isEntryValid
)
data class CharacterRarityDetails(
@@ -121,31 +143,61 @@ data class CharacterRarityDetails(
val rarityId: Int = 0
)
data class CharacterVisionDetails(
val characterId: Int = 0,
val visionId: Int = 0
)
data class CharacterWeaponDetails(
val characterId: Int = 0,
val weaponId: Int = 0
)
fun CharacterRarityDetails.toCharacterRarity(characterId: Int = 0, rarityId: Int = 0): CharacterRarityCrossRef = CharacterRarityCrossRef(
characterId = characterId,
rarityId = rarityId
)
fun CharacterRarityCrossRef.toDetails(): CharacterRarityDetails = CharacterRarityDetails(
characterId = characterId,
rarityId = rarityId
)
fun CharacterRarityCrossRef.toUiState(isEntryValid: Boolean = false): CharacterUiStateEdit = CharacterUiStateEdit(
characterRarityDetails = this.toDetails(),
isEntryValid = isEntryValid
)
data class CharacterVisionDetails(
val characterId: Int = 0,
val visionId: Int = 0
)
fun CharacterVisionDetails.toCharacterVision(characterId: Int = 0, visionId: Int = 0): CharacterVisionCrossRef = CharacterVisionCrossRef(
characterId = characterId,
visionId = visionId
)
fun CharacterVisionCrossRef.toDetails(): CharacterVisionDetails = CharacterVisionDetails(
characterId = characterId,
visionId = visionId
)
fun CharacterVisionCrossRef.toUiState(isEntryValid: Boolean = false): CharacterUiStateEdit = CharacterUiStateEdit(
characterVisionDetails = this.toDetails(),
isEntryValid = isEntryValid
)
data class CharacterWeaponDetails(
val characterId: Int = 0,
val weaponId: Int = 0
)
fun CharacterWeaponDetails.toCharacterWeapon(characterId: Int = 0, weaponId: Int = 0): CharacterWeaponCrossRef = CharacterWeaponCrossRef(
characterId = characterId,
weaponId = weaponId
)
fun CharacterWeaponCrossRef.toDetails(): CharacterWeaponDetails = CharacterWeaponDetails(
characterId = characterId,
weaponId = weaponId
)
fun CharacterWeaponCrossRef.toUiState(isEntryValid: Boolean = false): CharacterUiStateEdit = CharacterUiStateEdit(
characterWeaponDetails = this.toDetails(),
isEntryValid = isEntryValid
)
data class CharacterRarityUiState(
val characterRarityDetails: CharacterRarityDetails = CharacterRarityDetails(),
val isEntryValid: Boolean = false
@@ -161,41 +213,11 @@ data class CharacterWeaponUiState(
val isEntryValid: Boolean = false
)
fun CharacterRarityCrossRef.toDetails(): CharacterRarityDetails = CharacterRarityDetails(
characterId = characterId,
rarityId = rarityId
)
//fun List<Rarity>.toDetails(): CharacterRarityDetails = CharacterRarityDetails(
// rarityId = rarityId
//)
fun CharacterRarityCrossRef.toUiState(isEntryValid: Boolean = false): CharacterRarityUiState = CharacterRarityUiState(
characterRarityDetails = this.toDetails(),
isEntryValid = isEntryValid
)
fun CharacterVisionCrossRef.toDetails(): CharacterVisionDetails = CharacterVisionDetails(
characterId = characterId,
visionId = visionId
)
fun CharacterVisionCrossRef.toUiState(isEntryValid: Boolean = false): CharacterVisionUiState = CharacterVisionUiState(
characterVisionDetails = this.toDetails(),
isEntryValid = isEntryValid
)
fun CharacterWeaponCrossRef.toDetails(): CharacterWeaponDetails = CharacterWeaponDetails(
characterId = characterId,
weaponId = weaponId
)
fun CharacterWeaponCrossRef.toUiState(isEntryValid: Boolean = false): CharacterWeaponUiState = CharacterWeaponUiState(
characterWeaponDetails = this.toDetails(),
isEntryValid = isEntryValid
)
fun List<Rarity>.toDetails(): CharacterRarityDetails = CharacterRarityDetails(
rarityId = rarityId
)
fun List<Rarity>.toUiState(isEntryValid: Boolean = false): CharacterRarityUiState = CharacterRarityUiState(
characterRarityDetails = this.toDetails(),
isEntryValid = isEntryValid
)
//fun List<Rarity>.toUiState(isEntryValid: Boolean = false): CharacterRarityUiState = CharacterRarityUiState(
// characterRarityDetails = this.toDetails(),
// isEntryValid = isEntryValid
//)

View File

@@ -15,7 +15,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
@@ -30,16 +29,10 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
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
@@ -47,12 +40,9 @@ import androidx.navigation.NavController
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import com.example.android_genshin.composeui.navigation.Screen
import com.example.android_genshin.database.AppDatabase
import com.example.android_genshin.database.entities.model.Character
import com.example.android_genshin.ui.theme.Android_GenshinTheme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -70,7 +60,7 @@ fun CharacterList(
FloatingActionButton(
onClick = {
val route = Screen.CharacterEdit.route.replace("{id}", 0.toString())
navController.navigate(route)
navController?.navigate(route)
},
containerColor = MaterialTheme.colorScheme.primary,
) {
@@ -95,12 +85,12 @@ fun CharacterList(
},
onDeleteClick = { character: Character ->
coroutineScope.launch {
viewModel.removeCharacter(character.)
viewModel.removeCharacter(character)
}
},
onEditClick = { uid: Int ->
val route = Screen.CharacterEdit.route.replace("{id}", uid.toString())
navController.navigate(route)
navController?.navigate(route)
},
)
}

View File

@@ -8,9 +8,6 @@ import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.android_genshin.database.entities.model.Character
import com.example.android_genshin.database.entities.model.CharacterRarityCrossRef
import com.example.android_genshin.database.entities.model.CharacterVisionCrossRef
import com.example.android_genshin.database.entities.model.CharacterWeaponCrossRef
import com.example.android_genshin.database.entities.model.CharacterWithRarities
import com.example.android_genshin.database.entities.model.CharacterWithVisions
import com.example.android_genshin.database.entities.model.CharacterWithWeapons
@@ -69,55 +66,15 @@ class CharacterListViewModel(
rarities: List<Rarity>,
visions: List<Vision>,
weapons: List<Weapon>) {
val byteArrayImage = ByteArray(1)
if (rarities.isEmpty())
return
if (visions.isEmpty())
return
if (weapons.isEmpty())
return
val byteArrayImage = ByteArray(100)
val character = Character(0, "", userId, byteArrayImage)
val characterId = characterRepository.insertCharacter(character)
rarities.forEach { rarity ->
characterRarityRepository.insertCharacterRarity(
CharacterRarityCrossRef(
character.characterId,
rarity.rarityId
)
)
}
visions.forEach { vision ->
characterVisionRepository.insertCharacterVision(
CharacterVisionCrossRef(
character.characterId,
vision.visionId
)
)
}
weapons.forEach { weapon ->
characterWeaponRepository.insertCharacterWeapon(
CharacterWeaponCrossRef(
character.characterId,
weapon.weaponId
)
)
}
}
suspend fun removeCharacter(
character: Character) {
characterRepository.deleteCharacter(character)
}
suspend fun updateCharacter(character: Character,
rarity: Rarity,
vision: Vision,
weapon: Weapon) {
characterRepository.updateCharacter(character)
characterRarityRepository.updateCharacterRarity(CharacterRarityCrossRef(character.characterId, rarity.rarityId))
characterVisionRepository.updateCharacterVision(CharacterVisionCrossRef(character.characterId, vision.visionId))
characterWeaponRepository.updateCharacterWeapon(CharacterWeaponCrossRef(character.characterId, weapon.weaponId))
}
}
data class CharacterListUiState(

View File

@@ -1,4 +1,120 @@
package com.example.android_genshin.database.entities.composeui
class ImageUploader {
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.example.android_genshin.R
import java.io.ByteArrayOutputStream
@Composable
fun ImageUploader(
bitmap: Bitmap?,
onResult: (ByteArray) -> Unit
) {
val context = LocalContext.current
val title: String = if (bitmap == null) {
stringResource(R.string.not_uploaded)
} else {
stringResource(R.string.size, bitmap.width, bitmap.height)
}
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.GetContent(),
onResult = { uri: Uri? ->
uri?.let {
val inputStream = context.contentResolver.openInputStream(uri)
val newBitmap: Bitmap = BitmapFactory.decodeStream(inputStream)
val scaledBitmap = resizeBitmapWithAspectRatio(newBitmap, 200)
val stream = ByteArrayOutputStream()
scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream)
onResult(stream.toByteArray())
}
}
)
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.background(
color = MaterialTheme.colorScheme.surface,
shape = RoundedCornerShape(10.dp)
)
.aspectRatio(1f)
.border(
width = 1.dp,
color = MaterialTheme.colorScheme.primary,
shape = RoundedCornerShape(10.dp)
)
.clickable { launcher.launch("image/*") }
.padding(16.dp)
) {
if (bitmap != null) {
Image(
bitmap = bitmap.asImageBitmap(),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxSize()
.clip(shape = RoundedCornerShape(10.dp))
)
} else {
Image(
painter = painterResource(id = R.drawable.xiao_wbgr),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxSize()
.clip(shape = RoundedCornerShape(10.dp))
)
}
}
Spacer(modifier = Modifier.height(8.dp))
Text(
text = title,
color = MaterialTheme.colorScheme.onBackground
)
}
}
fun resizeBitmapWithAspectRatio(bitmap: Bitmap, maxHeight: Int): Bitmap {
if (bitmap.height <= maxHeight) {
return bitmap
}
val aspectRatio = bitmap.width.toFloat() / bitmap.height
val newWidth = (maxHeight * aspectRatio).toInt()
return Bitmap.createScaledBitmap(bitmap, newWidth, maxHeight, true)
}

View File

@@ -0,0 +1,38 @@
package com.example.android_genshin.database.entities.composeui
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.android_genshin.database.entities.model.Rarity
import com.example.android_genshin.database.entities.repository.RarityRepository
import kotlinx.coroutines.launch
class RarityDropDownViewModel(
private val rarityRepository: RarityRepository
) : ViewModel() {
var raritiesListUiState by mutableStateOf(RaritiesListUiState())
private set
var rarityUiState by mutableStateOf(RarityUiState())
private set
init {
viewModelScope.launch {
raritiesListUiState = RaritiesListUiState(rarityRepository.getAllRarities())
}
}
fun updateUiState(rarity: Rarity) {
rarityUiState = RarityUiState(
rarity = rarity
)
}
}
data class RaritiesListUiState(val rarityList: List<Rarity> = listOf())
data class RarityUiState(val rarity: Rarity? = null)
fun Rarity.toUiState() = RarityUiState(rarity = Rarity(rarityId = rarityId, rarityName = rarityName))

View File

@@ -0,0 +1,89 @@
package com.example.android_genshin.database.entities.composeui
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.android_genshin.R
import com.example.android_genshin.ui.theme.Android_GenshinTheme
import kotlinx.coroutines.launch
@Composable
fun RarityEdit(
navController: NavController,
viewModel: RarityEditViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val coroutineScope = rememberCoroutineScope()
RarityEdit(
rarityUiState = viewModel.rarityUiState,
onClick = {
coroutineScope.launch {
viewModel.saveRarity()
navController.popBackStack()
}
},
onUpdate = viewModel::updateUiState
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun RarityEdit(
rarityUiState: RarityUiStateEdit,
onClick: () -> Unit,
onUpdate: (RarityDetails) -> Unit
) {
Column(
Modifier
.fillMaxWidth()
.padding(all = 10.dp)
) {
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = rarityUiState.rarityDetails.rarityName,
onValueChange = { onUpdate(rarityUiState.rarityDetails.copy(rarityName = it)) },
label = { Text(stringResource(id = R.string.rarity_rarityName)) },
singleLine = true
)
Button(
onClick = onClick,
enabled = rarityUiState.isEntryValid,
shape = MaterialTheme.shapes.small,
modifier = Modifier.fillMaxWidth()
) {
Text(text = stringResource(R.string.character_save_button))
}
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun RarityEditPreview() {
Android_GenshinTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
RarityEdit(
rarityUiState = RarityUiStateEdit(),
onClick = {},
onUpdate = {},
)
}
}
}

View File

@@ -0,0 +1,81 @@
package com.example.android_genshin.database.entities.composeui
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.android_genshin.database.entities.model.Rarity
import com.example.android_genshin.database.entities.repository.RarityRepository
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
class RarityEditViewModel(
savedStateHandle: SavedStateHandle,
private val rarityRepository: RarityRepository
) : ViewModel() {
var rarityUiState by mutableStateOf(RarityUiStateEdit())
private val rarityUid : Int = checkNotNull(savedStateHandle["id"])
init {
viewModelScope.launch {
if(rarityUid > 0) {
rarityUiState = rarityRepository.getRarity(rarityUid)
.filterNotNull()
.first()
.toUiState(true)
}
}
}
fun updateUiState(rarityDetails : RarityDetails) {
rarityUiState = RarityUiStateEdit(
rarityDetails = rarityDetails,
isEntryValid = validateInput(rarityDetails)
)
}
suspend fun saveRarity() {
if (validateInput()) {
if (rarityUid > 0) {
rarityRepository.updateRarity(rarityUiState.rarityDetails.toRarity(rarityUid))
} else {
rarityRepository.insertRarity(rarityUiState.rarityDetails.toRarity())
}
}
}
private fun validateInput(uiState: RarityDetails = rarityUiState.rarityDetails): Boolean {
return with(uiState) {
rarityName.isNotBlank()
}
}
}
data class RarityUiStateEdit(
val rarityDetails: RarityDetails = RarityDetails(),
val isEntryValid: Boolean = false
)
data class RarityDetails(
val rarityId: Int = 0,
val rarityName: String = ""
)
fun RarityDetails.toRarity(uid: Int = 0): Rarity = Rarity(
rarityId = uid,
rarityName = rarityName
)
fun Rarity.toDetails(): RarityDetails = RarityDetails(
rarityId = rarityId,
rarityName = rarityName
)
fun Rarity.toUiState(isEntryValid: Boolean = false): RarityUiStateEdit = RarityUiStateEdit(
rarityDetails = this.toDetails(),
isEntryValid = isEntryValid
)

View File

@@ -1,8 +1,6 @@
package com.example.android_genshin.database.entities.composeui
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -13,49 +11,34 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
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 androidx.navigation.NavController
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import com.example.android_genshin.composeui.navigation.Screen
import com.example.android_genshin.database.AppDatabase
import com.example.android_genshin.database.entities.model.CharacterWithRarities
import com.example.android_genshin.database.entities.model.Rarity
import com.example.android_genshin.ui.theme.Android_GenshinTheme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RarityList(
characterWithRarities: CharacterWithRarities,
navController: NavController,
viewModel: RarityListViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
@@ -98,11 +81,7 @@ fun RarityList(
coroutineScope.launch {
viewModel.deleteRarity(rarity)
}
},
onEditClick = { uid: Int ->
val route = Screen.RarityEdit.route.replace("{id}", uid.toString())
navController.navigate(route)
},
}
)
}
}
@@ -111,8 +90,7 @@ fun RarityList(
private fun RarityList(
modifier: Modifier = Modifier,
pagingRarity: LazyPagingItems<Rarity>,
onDeleteClick: (rarity: Rarity) -> Unit,
onEditClick: (rarity: Int) -> Unit
onDeleteClick: (rarity: Rarity) -> Unit
) {
Column(
modifier = modifier
@@ -133,8 +111,7 @@ private fun RarityList(
color = MaterialTheme.colorScheme.onBackground,
shape = RoundedCornerShape(16.dp)
),
onDeleteClick = onDeleteClick,
onEditClick = onEditClick,
onDeleteClick = onDeleteClick
)
}
}
@@ -146,8 +123,7 @@ private fun RarityList(
private fun RarityListItem(
rarity: Rarity,
modifier: Modifier = Modifier,
onDeleteClick: (rarity: Rarity) -> Unit,
onEditClick: (rarity: Int) -> Unit
onDeleteClick: (rarity: Rarity) -> Unit
) {
Box(
modifier = modifier
@@ -166,16 +142,6 @@ private fun RarityListItem(
// Добавляем пустое пространство для разделения текста и кнопки
Spacer(modifier = Modifier.weight(1f))
IconButton(
onClick = { onEditClick(rarity.rarityId) },
modifier = Modifier.size(24.dp)
) {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = "Редактировать",
tint = MaterialTheme.colorScheme.onBackground,
)
}
IconButton(
onClick = { onDeleteClick(rarity) },
modifier = Modifier.size(24.dp)

View File

@@ -1,13 +1,10 @@
package com.example.android_genshin.database.entities.composeui
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -16,25 +13,25 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.android_genshin.R
import com.example.android_genshin.database.AppDatabase
import com.example.android_genshin.database.entities.model.Rarity
import com.example.android_genshin.ui.theme.Android_GenshinTheme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun RarityView(id: Int) {
fun RarityView(navController: NavController,
viewModel: RarityEditViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val context = LocalContext.current
val rarity = remember {
mutableStateOf(Rarity(0, ""))
}
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
rarity.value = AppDatabase.getInstance(context).rarityDao().getByUid(id)
}
}
Column(
@@ -49,17 +46,4 @@ fun RarityView(id: Int) {
}
)
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun RarityViewPreview() {
Android_GenshinTheme {
Surface(
color = MaterialTheme.colorScheme.background
) {
RarityView(id = 0)
}
}
}

View File

@@ -0,0 +1,38 @@
package com.example.android_genshin.database.entities.composeui
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.android_genshin.database.entities.model.Vision
import com.example.android_genshin.database.entities.repository.VisionRepository
import kotlinx.coroutines.launch
class VisionDropDownViewModel(
private val visionRepository: VisionRepository
) : ViewModel() {
var visionsListUiState by mutableStateOf(VisionsListUiState())
private set
var visionUiState by mutableStateOf(VisionUiState())
private set
init {
viewModelScope.launch {
visionsListUiState = VisionsListUiState(visionRepository.getAllVisions())
}
}
fun updateUiState(vision: Vision) {
visionUiState = VisionUiState(
vision = vision
)
}
}
data class VisionsListUiState(val visionList: List<Vision> = listOf())
data class VisionUiState(val vision: Vision? = null)
fun Vision.toUiState() = VisionUiState(vision = Vision(visionId = visionId, visionName = visionName))

View File

@@ -1,78 +1,156 @@
package com.example.android_genshin.database.entities.composeui
import android.content.res.Configuration
import androidx.compose.foundation.background
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.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
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 androidx.navigation.NavController
import com.example.android_genshin.database.AppDatabase
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import com.example.android_genshin.composeui.navigation.Screen
import com.example.android_genshin.database.entities.model.Vision
import com.example.android_genshin.ui.theme.Android_GenshinTheme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun VisionList(navController: NavController?) {
val context = LocalContext.current
val visions = remember { mutableStateListOf<Vision>() }
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
AppDatabase.getInstance(context).visionDao().getAll().collect { data ->
visions.clear()
visions.addAll(data)
}
fun VisionList(navController: NavController,
viewModel: VisionListViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val coroutineScope = rememberCoroutineScope()
val visionPagingItems = viewModel.visionPagerState.visionPagingData.collectAsLazyPagingItems()
fun findVisions() {
coroutineScope.launch {
viewModel.findVisions()
}
}
Box(
modifier = Modifier
.fillMaxWidth()
.padding(all = 10.dp)
.background(
color = MaterialTheme.colorScheme.background,
shape = RoundedCornerShape(16.dp)
LaunchedEffect(1) {
findVisions()
}
Scaffold(
topBar = {},
floatingActionButton = {
FloatingActionButton(
onClick = {
val route = Screen.RarityEdit.route.replace("{id}", 0.toString())
navController.navigate(route)
},
containerColor = MaterialTheme.colorScheme.primary,
) {
Icon(
Icons.Filled.Add,
"Добавить",
tint = MaterialTheme.colorScheme.onPrimary
)
}
}
) { innerPadding ->
VisionList(
modifier = Modifier
.padding(innerPadding)
.fillMaxSize(),
pagingVision = visionPagingItems,
onDeleteClick = { vision: Vision ->
coroutineScope.launch {
viewModel.deleteVision(vision)
}
}
)
}
}
@Composable
private fun VisionList(
modifier: Modifier = Modifier,
pagingVision: LazyPagingItems<Vision>,
onDeleteClick: (vision: Vision) -> Unit
) {
Column(
modifier = modifier
) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(all = 10.dp)
) {
items(visions) { vision ->
Text(
text = "${vision.visionId}: ${vision.visionName}",
color = MaterialTheme.colorScheme.onBackground
)
items(pagingVision.itemCount) { index ->
val vision = pagingVision[index]
if (vision != null) {
VisionListItem(
vision = vision,
modifier = Modifier
.padding(vertical = 7.dp)
.background(
color = MaterialTheme.colorScheme.onBackground,
shape = RoundedCornerShape(16.dp)
),
onDeleteClick = onDeleteClick
)
}
}
}
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun VisionListPreview() {
Android_GenshinTheme {
Surface(
color = MaterialTheme.colorScheme.background
private fun VisionListItem(
vision: Vision,
modifier: Modifier = Modifier,
onDeleteClick: (vision: Vision) -> Unit
) {
Box(
modifier = modifier
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
VisionList(navController = null)
Text(
"${vision.visionName}",
color = MaterialTheme.colorScheme.onBackground
)
// Добавляем пустое пространство для разделения текста и кнопки
Spacer(modifier = Modifier.weight(1f))
IconButton(
onClick = { onDeleteClick(vision) },
modifier = Modifier.size(24.dp)
) {
Icon(
imageVector = Icons.Default.Delete,
contentDescription = "Удалить",
tint = MaterialTheme.colorScheme.onSecondary,
)
}
}
}
}

View File

@@ -0,0 +1,38 @@
package com.example.android_genshin.database.entities.composeui
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.android_genshin.database.entities.model.Weapon
import com.example.android_genshin.database.entities.repository.WeaponRepository
import kotlinx.coroutines.launch
class WeaponDropDownViewModel(
private val weaponRepository: WeaponRepository
) : ViewModel() {
var weaponsListUiState by mutableStateOf(WeaponsListUiState())
private set
var weaponUiState by mutableStateOf(WeaponUiState())
private set
init {
viewModelScope.launch {
weaponsListUiState = WeaponsListUiState(weaponRepository.getAllWeapons())
}
}
fun updateUiState(weapon: Weapon) {
weaponUiState = WeaponUiState(
weapon = weapon
)
}
}
data class WeaponsListUiState(val weaponList: List<Weapon> = listOf())
data class WeaponUiState(val weapon: Weapon? = null)
fun Weapon.toUiState() = WeaponUiState(weapon = Weapon(weaponId = weaponId, weaponName = weaponName, weaponType = weaponType, weaponDamage = weaponDamage, image = image))

View File

@@ -1,4 +1,128 @@
package com.example.android_genshin.database.entities.composeui
class WeaponEdit {
import android.content.res.Configuration
import android.graphics.BitmapFactory
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MaterialTheme.colorScheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.android_genshin.R
import com.example.android_genshin.ui.theme.Android_GenshinTheme
import kotlinx.coroutines.launch
@Composable
fun WeaponEdit(
navController: NavController,
viewModel: WeaponEditViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val coroutineScope = rememberCoroutineScope()
WeaponEdit(
weaponUiState = viewModel.weaponUiState,
onClick = {
coroutineScope.launch {
viewModel.saveWeapon()
navController.popBackStack()
}
},
onUpdate = viewModel::updateUiState
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun WeaponEdit(
weaponUiState: WeaponUiStateEdit,
onClick: () -> Unit,
onUpdate: (WeaponDetails) -> Unit
) {
Column(
Modifier
.fillMaxWidth()
.padding(all = 10.dp)
) {
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = weaponUiState.weaponDetails.weaponName,
onValueChange = { onUpdate(weaponUiState.weaponDetails.copy(weaponName = it)) },
label = { Text(stringResource(id = R.string.weapon_weaponName)) },
singleLine = true
)
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = weaponUiState.weaponDetails.weaponType,
onValueChange = { onUpdate(weaponUiState.weaponDetails.copy(weaponType = it)) },
label = { Text(stringResource(id = R.string.weapon_weaponType)) },
singleLine = true
)
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = weaponUiState.weaponDetails.weaponDamage.toString(),
onValueChange = { newValue ->
val parsedDamage = newValue.toInt()
onUpdate(weaponUiState.weaponDetails.copy(weaponDamage = parsedDamage))
},
label = { Text(stringResource(id = R.string.weapon_weaponDamage)) },
singleLine = true,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
visualTransformation = VisualTransformation.None // Отключает маскировку
)
if (weaponUiState.weaponDetails.image != null)
ImageUploader(
bitmap = BitmapFactory.decodeByteArray(
weaponUiState.weaponDetails.image,
0,
weaponUiState.weaponDetails.image.size
),
onResult = { byteArray: ByteArray? ->
onUpdate(
weaponUiState.weaponDetails.copy(
image = byteArray
)
)
}
)
Button(
onClick = onClick,
enabled = weaponUiState.isEntryValid,
shape = MaterialTheme.shapes.small,
modifier = Modifier.fillMaxWidth()
) {
Text(text = stringResource(R.string.character_save_button))
}
}
}
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun WeaponEditPreview() {
Android_GenshinTheme {
Surface(
color = colorScheme.background
) {
WeaponEdit(
weaponUiState = WeaponUiStateEdit(),
onClick = {},
onUpdate = {},
)
}
}
}

View File

@@ -63,7 +63,6 @@ data class WeaponUiStateEdit(
)
data class WeaponDetails(
val weaponId: Int = 0,
val weaponName: String = "",
val weaponType: String = "",
val weaponDamage: Int = 0,
@@ -79,7 +78,6 @@ fun WeaponDetails.toWeapon(uid: Int = 0): Weapon = Weapon(
)
fun Weapon.toDetails(): WeaponDetails = WeaponDetails(
weaponId = weaponId,
weaponName = weaponName,
weaponType = weaponType,
weaponDamage = weaponDamage,

View File

@@ -17,14 +17,14 @@ class WeaponViewModel(
) : ViewModel() {
private val weaponUid: Int = checkNotNull(savedStateHandle["id"])
val weaponUiState: StateFlow<WeaponUiState> = weaponRepository.getWeapon(weaponUid)
val weaponUiState: StateFlow<WeaponViewUiState> = weaponRepository.getWeapon(weaponUid)
.map {
WeaponUiState(it)
WeaponViewUiState(it)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
initialValue = WeaponUiState()
initialValue = WeaponViewUiState()
)
}
data class WeaponUiState(val weapon: Weapon? = null)
data class WeaponViewUiState(val weapon: Weapon? = null)

View File

@@ -12,13 +12,13 @@ import kotlinx.coroutines.flow.Flow
@Dao
interface RarityDao {
@Query("SELECT * FROM rarities ORDER BY rarityName")
fun getAll(): Flow<List<Rarity>>
fun getAll(): List<Rarity>
@Query("select * from rarities order by rarityName")
fun getAllRaritiesPaged(): PagingSource<Int, Rarity>
@Query("select * from rarities where rarities.rarityId = :uid")
suspend fun getByUid(uid: Int?): Rarity
fun getByUid(uid: Int?): Flow<Rarity>
@Insert
suspend fun insert(rarity: Rarity)

View File

@@ -7,12 +7,11 @@ import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.example.android_genshin.database.entities.model.Vision
import kotlinx.coroutines.flow.Flow
@Dao
interface VisionDao {
@Query("SELECT * FROM visions ORDER BY visionName")
fun getAll(): Flow<List<Vision>>
fun getAll(): List<Vision>
@Query("select * from visions where visions.visionId = :uid")
suspend fun getByUid(uid: Int): Vision

View File

@@ -12,13 +12,13 @@ import kotlinx.coroutines.flow.Flow
@Dao
interface WeaponDao {
@Query("SELECT * FROM weapons ORDER BY weaponName")
fun getAll(): Flow<List<Weapon>>
fun getAll(): List<Weapon>
@Query("select * from weapons order by weaponName")
fun getAllWeaponsPaged(): PagingSource<Int, Weapon>
@Query("select * from weapons where weapons.weaponId = :uid")
suspend fun getByUid(uid: Int): Weapon
fun getByUid(uid: Int): Flow<Weapon>
@Insert
suspend fun insert(weapon: Weapon)

View File

@@ -6,11 +6,11 @@ import com.example.android_genshin.database.entities.model.Rarity
import kotlinx.coroutines.flow.Flow
class OfflineRarityRepository(private val rarityDao: RarityDao) : RarityRepository {
override fun getAllRarities(): Flow<List<Rarity>> = rarityDao.getAll()
override fun getAllRarities(): List<Rarity> = rarityDao.getAll()
override fun getAllRaritiesPaged(): PagingSource<Int, Rarity> = rarityDao.getAllRaritiesPaged()
override suspend fun getRarity(uid: Int): Rarity = rarityDao.getByUid(uid)
override fun getRarity(uid: Int): Flow<Rarity> = rarityDao.getByUid(uid)
override suspend fun insertRarity(rarity: Rarity) = rarityDao.insert(rarity)

View File

@@ -3,10 +3,9 @@ package com.example.android_genshin.database.entities.repository
import androidx.paging.PagingSource
import com.example.android_genshin.database.entities.dao.VisionDao
import com.example.android_genshin.database.entities.model.Vision
import kotlinx.coroutines.flow.Flow
class OfflineVisionRepository(private val visionDao: VisionDao) : VisionRepository {
override fun getAllVisions(): Flow<List<Vision>> = visionDao.getAll()
override fun getAllVisions(): List<Vision> = visionDao.getAll()
override fun getAllVisionsPaged(): PagingSource<Int, Vision> = visionDao.getAllVisionsPaged()

View File

@@ -6,11 +6,11 @@ import com.example.android_genshin.database.entities.model.Weapon
import kotlinx.coroutines.flow.Flow
class OfflineWeaponRepository(private val weaponDao: WeaponDao) : WeaponRepository {
override fun getAllWeapons(): Flow<List<Weapon>> = weaponDao.getAll()
override fun getAllWeapons(): List<Weapon> = weaponDao.getAll()
override fun getAllWeaponsPaged(): PagingSource<Int, Weapon> = weaponDao.getAllWeaponsPaged()
override suspend fun getWeapon(uid: Int): Weapon = weaponDao.getByUid(uid)
override fun getWeapon(uid: Int): Flow<Weapon> = weaponDao.getByUid(uid)
override suspend fun insertWeapon(weapon: Weapon) = weaponDao.insert(weapon)

View File

@@ -5,9 +5,9 @@ import com.example.android_genshin.database.entities.model.Rarity
import kotlinx.coroutines.flow.Flow
interface RarityRepository {
fun getAllRarities(): Flow<List<Rarity>>
fun getAllRarities(): List<Rarity>
fun getAllRaritiesPaged(): PagingSource<Int, Rarity>
suspend fun getRarity(uid: Int): Rarity
fun getRarity(uid: Int): Flow<Rarity>
suspend fun insertRarity(rarity: Rarity)
suspend fun updateRarity(rarity: Rarity)
suspend fun deleteRarity(rarity: Rarity)

View File

@@ -2,10 +2,9 @@ package com.example.android_genshin.database.entities.repository
import androidx.paging.PagingSource
import com.example.android_genshin.database.entities.model.Vision
import kotlinx.coroutines.flow.Flow
interface VisionRepository {
fun getAllVisions(): Flow<List<Vision>>
fun getAllVisions(): List<Vision>
fun getAllVisionsPaged(): PagingSource<Int, Vision>
suspend fun getVision(uid: Int): Vision
suspend fun insertVision(vision: Vision)

View File

@@ -5,7 +5,7 @@ import com.example.android_genshin.database.entities.model.Weapon
import kotlinx.coroutines.flow.Flow
interface WeaponRepository {
fun getAllWeapons(): Flow<List<Weapon>>
fun getAllWeapons(): List<Weapon>
fun getAllWeaponsPaged(): PagingSource<Int, Weapon>
fun getWeapon(uid: Int): Flow<Weapon>
suspend fun insertWeapon(weapon: Weapon)

View File

@@ -19,6 +19,12 @@
<string name="weapon_view_title">Об оружии</string>
<string name="about_title">Genshin Wiki</string>
<string name="profile_title">Профиль пользователя</string>
<string name="character_rarity_not_select">Выбери редкость</string>
<string name="character_vision_not_select">Выбери Глаз Бога</string>
<string name="character_weapon_not_select">Выбери оружие</string>
<string name="character_save_button">Сохранить</string>
<string name="not_uploaded">Не загружена картинка</string>
<string name="size">Размер картинки</string>
<string name="about_text">
<p>Это текст <b>о нас</b>!</p>\n\n

View File

@@ -6,7 +6,7 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
org.gradle.jvmargs=-Xmx4608m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects