Skill dialog bottom sheet

Bottom sheet dialog for displaying skill information
Includes a new composable view: SkillDialog
This commit is contained in:
Hafiz 2025-06-04 15:38:14 -05:00 committed by Phillip Thelen
parent ef2229e438
commit 6913582b91
4 changed files with 282 additions and 13 deletions

View file

@ -0,0 +1,74 @@
package com.habitrpg.android.habitica.ui.fragments.skills
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.res.painterResource
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.ui.views.SkillDialog
import com.habitrpg.common.habitica.extensions.asPainter
import com.habitrpg.common.habitica.theme.HabiticaTheme
class SkillDialogBottomSheetFragment : BottomSheetDialogFragment() {
companion object {
private const val ARG_SKILL_TITLE = "skill_title"
private const val ARG_SKILL_DESCRIPTION = "skill_description"
private const val ARG_SKILL_MP_COST = "skill_mp_cost"
fun newInstance(
skillTitle: String,
skillDescription: String,
skillMpCost: String,
skillPath: String,
skillKey: String,
resourceIcon: Drawable,
onUseSkill: () -> Unit
): SkillDialogBottomSheetFragment {
return SkillDialogBottomSheetFragment().apply {
arguments = Bundle().apply {
putString(ARG_SKILL_TITLE, skillTitle)
putString(ARG_SKILL_DESCRIPTION, skillDescription)
putString(ARG_SKILL_MP_COST, skillMpCost)
}
this.resourceIcon = resourceIcon
this.skillKey = skillKey
this.skillPath = skillPath
this.onUseSkill = onUseSkill
}
}
}
var onUseSkill: (() -> Unit)? = null
private var resourceIcon: Drawable? = null
var skillKey = ""
var skillPath = ""
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View = ComposeView(requireContext()).apply {
setContent {
HabiticaTheme {
SkillDialog(
skillKey = skillKey,
skillPath = skillPath,
resourceIconPainter = resourceIcon?.asPainter() ?: painterResource(R.drawable.empty_slot),
title = requireArguments().getString(ARG_SKILL_TITLE) ?: "",
description = requireArguments().getString(ARG_SKILL_DESCRIPTION) ?: "",
mpCost = requireArguments().getString(ARG_SKILL_MP_COST) ?: "",
onUseSkill = {
onUseSkill?.invoke()
dismiss()
}
)
}
}
}
}

View file

@ -3,6 +3,7 @@ package com.habitrpg.android.habitica.ui.fragments.skills
import android.app.Activity
import android.content.Intent
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -31,6 +32,9 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
import javax.inject.Inject
import androidx.core.graphics.drawable.toDrawable
import com.habitrpg.common.habitica.extensions.loadImage
import com.habitrpg.common.habitica.views.PixelArtView
@AndroidEntryPoint
class SkillsFragment : BaseMainFragment<FragmentRecyclerviewBinding>() {
@ -99,21 +103,38 @@ class SkillsFragment : BaseMainFragment<FragmentRecyclerviewBinding>() {
}
private fun onSkillSelected(skill: Skill) {
when {
"special" == skill.habitClass -> {
selectedSkill = skill
val intent = Intent(mainActivity, SkillMemberActivity::class.java)
memberSelectionResult.launch(intent)
}
val context = context ?: return
val resourceIconDrawable: Drawable = HabiticaIconsHelper.imageOfMagic().toDrawable(context.resources)
val skillIdentifier = "shop_"
val bottomSheet = SkillDialogBottomSheetFragment.newInstance(
skillTitle = skill.text,
skillDescription = skill.notes ?: "",
skillKey = skill.key,
skillPath = skillIdentifier,
skillMpCost = "${skill.mana?.toInt() ?: 0} MP",
resourceIcon = resourceIconDrawable,
onUseSkill = {
when {
"special" == skill.habitClass -> {
selectedSkill = skill
val intent = Intent(mainActivity, SkillMemberActivity::class.java)
memberSelectionResult.launch(intent)
}
skill.target == "task" -> {
selectedSkill = skill
val intent = Intent(mainActivity, SkillTasksActivity::class.java)
taskSelectionResult.launch(intent)
}
else -> useSkill(skill)
}
}
)
bottomSheet.show(childFragmentManager, "SkillDialogBottomSheet")
skill.target == "task" -> {
selectedSkill = skill
val intent = Intent(mainActivity, SkillTasksActivity::class.java)
taskSelectionResult.launch(intent)
}
else -> useSkill(skill)
}
}
private fun displaySkillResult(

View file

@ -0,0 +1,164 @@
package com.habitrpg.android.habitica.ui.views
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
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.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
import com.habitrpg.android.habitica.R
import androidx.compose.foundation.layout.Column
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.ui.tooling.preview.Preview
import com.habitrpg.android.habitica.ui.theme.colors
import com.habitrpg.common.habitica.theme.HabiticaTheme
import androidx.compose.material3.Icon
import androidx.compose.foundation.layout.size
import androidx.compose.ui.res.painterResource
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.text.style.TextAlign
import com.habitrpg.android.habitica.ui.theme.pixelArtBackground
@Composable
fun SkillDialog(
modifier: Modifier = Modifier,
skillPath: String = "",
skillKey: String = "",
resourceIconPainter: Painter,
title: String,
description: String,
mpCost: String,
onUseSkill: () -> Unit,
) {
val colors = HabiticaTheme.colors
Box(
modifier
.fillMaxWidth()
.background(colors.windowBackground, shape = RoundedCornerShape(18.dp))
.padding(horizontal = 24.dp, vertical = 18.dp)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
Box(
Modifier
.padding(bottom = 12.dp)
.size(width = 38.dp, height = 6.dp)
.clip(CircleShape)
.background(colors.textSecondary)
)
Box(
Modifier
.size(54.dp)
.clip(RoundedCornerShape(12.dp))
.background(colors.pixelArtBackground(hasIcon = true)),
contentAlignment = Alignment.Center
) {
PixelArtView (
imageName = skillPath + skillKey,
modifier = Modifier.size(48.dp)
)
}
Spacer(Modifier.height(16.dp))
Text(
text = title,
fontSize = 22.sp,
fontWeight = FontWeight.Bold,
color = colors.textPrimary,
modifier = Modifier.padding(top = 2.dp)
)
Text(
text = description,
fontSize = 15.sp,
color = colors.textSecondary,
modifier = Modifier.padding(top = 8.dp),
textAlign = TextAlign.Center
)
Spacer(Modifier.height(18.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.clip(RoundedCornerShape(20.dp))
.background(Color(0xFF393F68))
.padding(horizontal = 20.dp, vertical = 8.dp)
) {
Icon(
painter = resourceIconPainter,
contentDescription = null,
tint = Color(0xFF77C7F7),
modifier = Modifier.size(22.dp)
)
Spacer(Modifier.width(8.dp))
Text(
text = mpCost,
color = Color.White,
fontWeight = FontWeight.Bold,
fontSize = 16.sp
)
}
Spacer(Modifier.height(28.dp))
Button(
onClick = onUseSkill,
colors = ButtonDefaults.buttonColors(
containerColor = Color(0xFFA380FC),
contentColor = Color.White
),
modifier = Modifier
.fillMaxWidth()
.height(48.dp)
.clip(RoundedCornerShape(12.dp))
) {
Text(
text = "Use Skill",
fontWeight = FontWeight.Bold,
fontSize = 17.sp
)
}
}
}
}
@Preview(showBackground = true, backgroundColor = 0xFF232136)
@Composable
fun PreviewSkillDialog() {
SkillDialog(
skillKey = "",
skillPath = "",
resourceIconPainter = painterResource(id = R.drawable.empty_slot),
title = "Title Skill",
description = "Skill Description",
mpCost = "10 MP",
onUseSkill = {
}
)
}

View file

@ -2,7 +2,11 @@ package com.habitrpg.common.habitica.extensions
import android.content.Context
import android.graphics.PorterDuff
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.graphics.painter.Painter
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
@ -22,3 +26,9 @@ fun Drawable.setTintWith(
DrawableCompat.setTint(this, color)
DrawableCompat.setTintMode(this, tintMode)
}
fun Drawable?.asPainter(): Painter? {
return if (this is BitmapDrawable) {
BitmapPainter(this.bitmap.asImageBitmap())
} else null
}