Improve Talkback for tiles

Changes made:
- Shop items now read out name of item, price with currency, limited availability, items owned and locked state (previously just price)
- In equipment, name of item or none will be read out in addition to slot (previously just slot)
- In stable, hatchable pets will be read out as name of pet and "Hatch Pet" (previously unlabelled)
- In task creation, the click listener for the difficulty has been moved to the surrounding column so icon and text are grouped (previously icon was read as unlabeled and text was not clickable)
- In navigation drawer, the profile picture is read out as "Profile" (previously unlabelled)

My Habitica User-ID: 2bcd10dc-0ea9-4d95-b7a6-f55fa07af537
This commit is contained in:
AJ Keane 2025-05-31 16:21:03 +02:00 committed by Phillip Thelen
parent ff568fc68c
commit 07450f43ff
9 changed files with 152 additions and 96 deletions

View file

@ -33,7 +33,8 @@
android:id="@+id/avatarView" android:id="@+id/avatarView"
android:layout_width="70dp" android:layout_width="70dp"
android:layout_height="70dp" android:layout_height="70dp"
android:layout_gravity="center" /> android:layout_gravity="center"
android:contentDescription="@string/profile"/>
</com.habitrpg.android.habitica.ui.RoundedFrameLayout> </com.habitrpg.android.habitica.ui.RoundedFrameLayout>
<LinearLayout <LinearLayout

View file

@ -47,6 +47,7 @@
android:textColor="@color/text_quad" android:textColor="@color/text_quad"
android:paddingStart="2dp" android:paddingStart="2dp"
android:paddingEnd="2dp" android:paddingEnd="2dp"
android:accessibilityTraversalAfter="@id/container"
/> />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
@ -89,5 +90,6 @@
android:layout_gravity="end" android:layout_gravity="end"
android:visibility="gone" android:visibility="gone"
android:layout_alignEnd="@id/container" android:layout_alignEnd="@id/container"
android:importantForAccessibility="no"
style="@style/CountLabel"/> style="@style/CountLabel"/>
</RelativeLayout> </RelativeLayout>

View file

@ -201,6 +201,8 @@ class PetDetailRecyclerAdapter :
binding.eggView.startAnimation(Animations.bobbingAnimation(4f)) binding.eggView.startAnimation(Animations.bobbingAnimation(4f))
binding.hatchingPotionView.startAnimation(Animations.bobbingAnimation(-4f)) binding.hatchingPotionView.startAnimation(Animations.bobbingAnimation(-4f))
binding.root.contentDescription = "${item.text}, ${itemView.context.getString(R.string.hatch_pet)}"
} }
override fun onClick(p0: View?) { override fun onClick(p0: View?) {

View file

@ -337,7 +337,7 @@ fun AvatarOverviewView(
userViewModel.updateUser("preferences.autoEquip", it) userViewModel.updateUser("preferences.autoEquip", it)
}) })
} }
EquipmentOverviewView(user?.items?.gear?.equipped, battleGearTwoHanded, { type, equipped -> EquipmentOverviewView(user?.items?.gear?.owned, user?.items?.gear?.equipped, battleGearTwoHanded, { type, equipped ->
onEquipmentTap(type, equipped, false) onEquipmentTap(type, equipped, false)
}) })
Row( Row(
@ -362,7 +362,7 @@ fun AvatarOverviewView(
userViewModel.updateUser("preferences.costume", it) userViewModel.updateUser("preferences.costume", it)
}) })
} }
EquipmentOverviewView(user?.items?.gear?.costume, costumeTwoHanded, { type, equipped -> EquipmentOverviewView(user?.items?.gear?.owned, user?.items?.gear?.costume, costumeTwoHanded, { type, equipped ->
onEquipmentTap(type, equipped, true) onEquipmentTap(type, equipped, true)
}, modifier = Modifier.alpha(if (user?.preferences?.costume == true) 1.0f else 0.5f)) }, modifier = Modifier.alpha(if (user?.preferences?.costume == true) 1.0f else 0.5f))
} }

View file

@ -8,6 +8,9 @@ import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.RowShopitemBinding import com.habitrpg.android.habitica.databinding.RowShopitemBinding
import com.habitrpg.android.habitica.extensions.getImpreciseRemainingString
import com.habitrpg.android.habitica.extensions.getRemainingString
import com.habitrpg.android.habitica.extensions.getShortRemainingString
import com.habitrpg.android.habitica.models.shops.ShopItem import com.habitrpg.android.habitica.models.shops.ShopItem
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
import com.habitrpg.common.habitica.extensions.dpToPx import com.habitrpg.common.habitica.extensions.dpToPx
@ -52,6 +55,8 @@ class ShopItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), Vi
this.item = item this.item = item
binding.buyButton.visibility = View.VISIBLE binding.buyButton.visibility = View.VISIBLE
var contentDescription = item.text
binding.imageView.loadImage(item.imageName?.replace("_locked", "")) binding.imageView.loadImage(item.imageName?.replace("_locked", ""))
binding.itemDetailIndicator.text = null binding.itemDetailIndicator.text = null
@ -66,10 +71,12 @@ class ShopItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), Vi
} }
binding.priceLabel.visibility = View.VISIBLE binding.priceLabel.visibility = View.VISIBLE
binding.unlockLabel.visibility = View.GONE binding.unlockLabel.visibility = View.GONE
contentDescription += ", ${item.value} ${binding.priceLabel.currencyContentDescription}"
} else { } else {
binding.unlockLabel.text = lockedReason binding.unlockLabel.text = lockedReason
binding.priceLabel.visibility = View.GONE binding.priceLabel.visibility = View.GONE
binding.unlockLabel.visibility = View.VISIBLE binding.unlockLabel.visibility = View.VISIBLE
contentDescription += ", $lockedReason"
} }
val isLimited = item.isLimited || item.availableUntil != null val isLimited = item.isLimited || item.availableUntil != null
if (numberOwned > 0) { if (numberOwned > 0) {
@ -82,10 +89,12 @@ class ShopItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), Vi
binding.itemDetailIndicator.setTextColor(ContextCompat.getColor(context, R.color.text_quad)) binding.itemDetailIndicator.setTextColor(ContextCompat.getColor(context, R.color.text_quad))
} }
binding.itemDetailIndicator.visibility = View.VISIBLE binding.itemDetailIndicator.visibility = View.VISIBLE
contentDescription += ", ${context.getString(R.string.owned)}: $numberOwned"
} else if (item.locked) { } else if (item.locked) {
binding.itemDetailIndicator.background = binding.itemDetailIndicator.background =
AppCompatResources.getDrawable(context, if (isLimited) R.drawable.shop_locked_limited else R.drawable.shop_locked) AppCompatResources.getDrawable(context, if (isLimited) R.drawable.shop_locked_limited else R.drawable.shop_locked)
binding.itemDetailIndicator.visibility = View.VISIBLE binding.itemDetailIndicator.visibility = View.VISIBLE
contentDescription += ", ${context.getString(R.string.locked)}"
} else if (isLimited) { } else if (isLimited) {
if (numberOwned == 0) { if (numberOwned == 0) {
binding.itemDetailIndicator.background = AppCompatResources.getDrawable(context, R.drawable.shop_limited) binding.itemDetailIndicator.background = AppCompatResources.getDrawable(context, R.drawable.shop_limited)
@ -93,6 +102,10 @@ class ShopItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), Vi
binding.itemDetailIndicator.background = AppCompatResources.getDrawable(context, R.drawable.pill_bg_purple_300) binding.itemDetailIndicator.background = AppCompatResources.getDrawable(context, R.drawable.pill_bg_purple_300)
} }
binding.itemDetailIndicator.visibility = View.VISIBLE binding.itemDetailIndicator.visibility = View.VISIBLE
item.availableUntil?.let {
contentDescription += ", ${it.getImpreciseRemainingString(context.resources)}"
}
} }
val limitedLeft = item.limitedNumberLeft ?: limitedNumberLeft val limitedLeft = item.limitedNumberLeft ?: limitedNumberLeft
@ -100,12 +113,14 @@ class ShopItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), Vi
binding.itemDetailIndicator.background = binding.itemDetailIndicator.background =
AppCompatResources.getDrawable(context, R.drawable.item_indicator_subscribe) AppCompatResources.getDrawable(context, R.drawable.item_indicator_subscribe)
binding.itemDetailIndicator.visibility = View.VISIBLE binding.itemDetailIndicator.visibility = View.VISIBLE
contentDescription += ", ${context.getString(R.string.locked)}"
} else if (item.key == "gem") { } else if (item.key == "gem") {
binding.itemDetailIndicator.background = binding.itemDetailIndicator.background =
AppCompatResources.getDrawable(context, R.drawable.pill_bg_green) AppCompatResources.getDrawable(context, R.drawable.pill_bg_green)
binding.itemDetailIndicator.text = "$limitedLeft" binding.itemDetailIndicator.text = "$limitedLeft"
binding.itemDetailIndicator.setTextColor(ContextCompat.getColor(context, R.color.white)) binding.itemDetailIndicator.setTextColor(ContextCompat.getColor(context, R.color.white))
binding.itemDetailIndicator.visibility = View.VISIBLE binding.itemDetailIndicator.visibility = View.VISIBLE
contentDescription += ", ${context.getString(R.string.gems_left_nomax, limitedLeft)}"
} }
if (binding.itemDetailIndicator.visibility == View.VISIBLE) { if (binding.itemDetailIndicator.visibility == View.VISIBLE) {
@ -120,6 +135,8 @@ class ShopItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), Vi
} }
binding.priceLabel.isLocked = item.locked || (!canBuy && item.currency == "gold") binding.priceLabel.isLocked = item.locked || (!canBuy && item.currency == "gold")
binding.container.contentDescription = contentDescription
} }
override fun onClick(view: View) { override fun onClick(view: View) {

View file

@ -11,6 +11,9 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -58,7 +61,16 @@ fun CurrencyText(
} else { } else {
value.toFloat() value.toFloat()
} }
Row(verticalAlignment = Alignment.CenterVertically, modifier = modifier) { val currencyName = when (currency) {
"gold" -> stringResource(R.string.gold_plural)
"gems" -> stringResource(R.string.gems)
"hourglasses" -> stringResource(R.string.mystic_hourglasses)
else -> ""
}
val amount = NumberAbbreviator.abbreviate(null, animatedValue, decimals, minForAbbreviation)
Row(verticalAlignment = Alignment.CenterVertically, modifier = modifier.semantics(mergeDescendants = true) {
contentDescription = "$amount $currencyName"
}) {
when (currency) { when (currency) {
"gold" -> HabiticaIconsHelper.imageOfGold() "gold" -> HabiticaIconsHelper.imageOfGold()
"gems" -> HabiticaIconsHelper.imageOfGem() "gems" -> HabiticaIconsHelper.imageOfGem()
@ -66,14 +78,14 @@ fun CurrencyText(
else -> null else -> null
}?.asImageBitmap()?.let { Image(it, null, Modifier.padding(end = 5.dp)) } }?.asImageBitmap()?.let { Image(it, null, Modifier.padding(end = 5.dp)) }
Text( Text(
NumberAbbreviator.abbreviate(null, animatedValue, decimals, minForAbbreviation), text = amount,
color = color =
when (currency) { when (currency) {
"gold" -> colorResource(R.color.text_gold) "gold" -> colorResource(R.color.text_gold)
"gems" -> colorResource(R.color.text_green) "gems" -> colorResource(R.color.text_green)
"hourglasses" -> colorResource(R.color.text_brand) "hourglasses" -> colorResource(R.color.text_brand)
else -> colorResource(R.color.text_primary) else -> colorResource(R.color.text_primary)
}, },
fontSize = fontSize, fontSize = fontSize,
fontWeight = FontWeight.SemiBold fontWeight = FontWeight.SemiBold
) )

View file

@ -31,7 +31,8 @@ class CurrencyView : androidx.appcompat.widget.AppCompatTextView {
configureCurrency() configureCurrency()
updateVisibility() updateVisibility()
} }
private var currencyContentDescription: String? = null var currencyContentDescription: String? = null
private set
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
val attributes = val attributes =

View file

@ -20,10 +20,15 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.isTraversalGroup
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.traversalIndex
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.models.inventory.Equipment
import com.habitrpg.android.habitica.models.user.Outfit import com.habitrpg.android.habitica.models.user.Outfit
import com.habitrpg.android.habitica.models.user.Preferences import com.habitrpg.android.habitica.models.user.Preferences
import com.habitrpg.android.habitica.ui.theme.colors import com.habitrpg.android.habitica.ui.theme.colors
@ -31,32 +36,36 @@ import com.habitrpg.android.habitica.ui.theme.pixelArtBackground
import com.habitrpg.android.habitica.ui.views.PixelArtView import com.habitrpg.android.habitica.ui.views.PixelArtView
import com.habitrpg.common.habitica.theme.HabiticaTheme import com.habitrpg.common.habitica.theme.HabiticaTheme
import com.habitrpg.common.habitica.theme.caption2 import com.habitrpg.common.habitica.theme.caption2
import io.realm.RealmList
@Composable @Composable
fun OverviewItem( fun OverviewItem(
text: String, text: String,
iconName: String?, iconName: String?,
itemName: String = "",
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
isTwoHanded: Boolean = false isTwoHanded: Boolean = false
) { ) {
val hasIcon = val hasIcon =
isTwoHanded || ( isTwoHanded || (
iconName?.isNotBlank() == true && iconName != "shirt_" && iconName?.isNotBlank() == true && iconName != "shirt_" &&
!iconName.endsWith( !iconName.endsWith(
"_none" "_none"
) && !iconName.endsWith("_base_0") && !iconName.endsWith("_") ) && !iconName.endsWith("_base_0") && !iconName.endsWith("_")
) )
val description = if (hasIcon) itemName else stringResource(R.string.none)
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier =
modifier modifier
.width(76.dp) .width(76.dp)
) { ) {
Box( Box(
Modifier Modifier
.size(76.dp) .size(76.dp)
.clip(MaterialTheme.shapes.small) .clip(MaterialTheme.shapes.small)
.background(HabiticaTheme.colors.pixelArtBackground(hasIcon)), .background(HabiticaTheme.colors.pixelArtBackground(hasIcon))
.semantics { contentDescription = description },
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
if (isTwoHanded) { if (isTwoHanded) {
@ -65,8 +74,8 @@ fun OverviewItem(
PixelArtView( PixelArtView(
imageName = iconName, imageName = iconName,
modifier = modifier =
Modifier Modifier
.size(76.dp) .size(76.dp)
) )
} else { } else {
Image(painterResource(R.drawable.empty_slot), null) Image(painterResource(R.drawable.empty_slot), null)
@ -77,13 +86,15 @@ fun OverviewItem(
style = HabiticaTheme.typography.caption2, style = HabiticaTheme.typography.caption2,
color = colorResource(R.color.text_secondary), color = colorResource(R.color.text_secondary),
textAlign = TextAlign.Center, textAlign = TextAlign.Center,
modifier = Modifier.padding(top = 4.dp) modifier = Modifier
.padding(top = 4.dp)
) )
} }
} }
@Composable @Composable
fun EquipmentOverviewView( fun EquipmentOverviewView(
owned: RealmList<Equipment>?,
outfit: Outfit?, outfit: Outfit?,
isUsingTwohanded: Boolean, isUsingTwohanded: Boolean,
onEquipmentTap: (String, String?) -> Unit, onEquipmentTap: (String, String?) -> Unit,
@ -92,79 +103,87 @@ fun EquipmentOverviewView(
Column( Column(
verticalArrangement = Arrangement.spacedBy(18.dp), verticalArrangement = Arrangement.spacedBy(18.dp),
modifier = modifier =
modifier modifier
.fillMaxWidth() .fillMaxWidth()
.clip(MaterialTheme.shapes.medium) .clip(MaterialTheme.shapes.medium)
.background(colorResource(R.color.equipment_column_background)) .background(colorResource(R.color.equipment_column_background))
.padding(12.dp) .padding(12.dp)
) { ) {
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
OverviewItem( OverviewItem(
stringResource(R.string.outfit_weapon), stringResource(R.string.outfit_weapon),
outfit?.weapon.let { "shop_$it" }, outfit?.weapon.let { "shop_$it" },
itemName = owned?.firstOrNull { it.key == outfit?.weapon }?.text ?: "",
modifier = modifier =
Modifier.clickable { Modifier.clickable {
onEquipmentTap("weapon", outfit?.weapon) onEquipmentTap("weapon", outfit?.weapon)
} }
) )
OverviewItem( OverviewItem(
stringResource(R.string.outfit_shield), stringResource(R.string.outfit_shield),
outfit?.shield.let { "shop_$it" }, outfit?.shield.let { "shop_$it" },
itemName = owned?.firstOrNull { it.key == outfit?.shield }?.text ?: "",
modifier = modifier =
Modifier.clickable { Modifier.clickable {
onEquipmentTap("shield", outfit?.shield) onEquipmentTap("shield", outfit?.shield)
}, },
isUsingTwohanded isUsingTwohanded
) )
OverviewItem( OverviewItem(
stringResource(R.string.outfit_head), stringResource(R.string.outfit_head),
outfit?.head.let { "shop_$it" }, outfit?.head.let { "shop_$it" },
itemName = owned?.firstOrNull { it.key == outfit?.head }?.text ?: "",
modifier = modifier =
Modifier.clickable { Modifier.clickable {
onEquipmentTap("head", outfit?.head) onEquipmentTap("head", outfit?.head)
} }
) )
OverviewItem( OverviewItem(
stringResource(R.string.outfit_armor), stringResource(R.string.outfit_armor),
outfit?.armor.let { "shop_$it" }, outfit?.armor.let { "shop_$it" },
itemName = owned?.firstOrNull { it.key == outfit?.armor }?.text ?: "",
modifier = modifier =
Modifier.clickable { Modifier.clickable {
onEquipmentTap("armor", outfit?.armor) onEquipmentTap("armor", outfit?.armor)
} }
) )
} }
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
OverviewItem( OverviewItem(
stringResource(R.string.outfit_headAccessory), stringResource(R.string.outfit_headAccessory),
outfit?.headAccessory.let { "shop_$it" }, outfit?.headAccessory.let { "shop_$it" },
itemName = owned?.firstOrNull { it.key == outfit?.headAccessory }?.text ?: "",
modifier = modifier =
Modifier.clickable { Modifier.clickable {
onEquipmentTap("headAccessory", outfit?.headAccessory) onEquipmentTap("headAccessory", outfit?.headAccessory)
} }
) )
OverviewItem( OverviewItem(
stringResource(R.string.outfit_body), stringResource(R.string.outfit_body),
outfit?.body.let { "shop_$it" }, outfit?.body.let { "shop_$it" },
itemName = owned?.firstOrNull { it.key == outfit?.body }?.text ?: "",
modifier = modifier =
Modifier.clickable { Modifier.clickable {
onEquipmentTap("body", outfit?.body) onEquipmentTap("body", outfit?.body)
} }
) )
OverviewItem( OverviewItem(
stringResource(R.string.outfit_back), stringResource(R.string.outfit_back),
outfit?.back.let { "shop_$it" }, outfit?.back.let { "shop_$it" },
itemName = owned?.firstOrNull { it.key == outfit?.back }?.text ?: "",
modifier = modifier =
Modifier.clickable { Modifier.clickable {
onEquipmentTap("back", outfit?.back) onEquipmentTap("back", outfit?.back)
} }
) )
OverviewItem( OverviewItem(
stringResource(R.string.outfit_eyewear), stringResource(R.string.outfit_eyewear),
outfit?.eyeWear.let { "shop_$it" }, outfit?.eyeWear.let { "shop_$it" },
itemName = owned?.firstOrNull { it.key == outfit?.eyeWear }?.text ?: "",
modifier = modifier =
Modifier.clickable { Modifier.clickable {
onEquipmentTap("eyewear", outfit?.eyeWear) onEquipmentTap("eyewear", outfit?.eyeWear)
} }
) )
} }
} }
@ -181,44 +200,44 @@ fun AvatarCustomizationOverviewView(
Column( Column(
verticalArrangement = Arrangement.spacedBy(18.dp), verticalArrangement = Arrangement.spacedBy(18.dp),
modifier = modifier =
modifier modifier
.fillMaxWidth() .fillMaxWidth()
.clip(MaterialTheme.shapes.medium) .clip(MaterialTheme.shapes.medium)
.background(colorResource(R.color.equipment_column_background)) .background(colorResource(R.color.equipment_column_background))
.padding(12.dp) .padding(12.dp)
) { ) {
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
OverviewItem( OverviewItem(
stringResource(R.string.avatar_shirt), stringResource(R.string.avatar_shirt),
preferences?.shirt.let { "icon_${preferences?.size}_shirt_$it" }, preferences?.shirt.let { "icon_${preferences?.size}_shirt_$it" },
modifier = modifier =
Modifier.clickable { Modifier.clickable {
onCustomizationTap("shirt", null) onCustomizationTap("shirt", null)
} }
) )
OverviewItem( OverviewItem(
stringResource(R.string.avatar_skin), stringResource(R.string.avatar_skin),
preferences?.skin.let { "icon_skin_$it" }, preferences?.skin.let { "icon_skin_$it" },
modifier = modifier =
Modifier.clickable { Modifier.clickable {
onCustomizationTap("skin", null) onCustomizationTap("skin", null)
} }
) )
OverviewItem( OverviewItem(
stringResource(R.string.avatar_hair_color), stringResource(R.string.avatar_hair_color),
if (preferences?.hair?.color != null && preferences.hair?.color != "") "icon_hair_bangs_1_" + preferences.hair?.color else "", if (preferences?.hair?.color != null && preferences.hair?.color != "") "icon_hair_bangs_1_" + preferences.hair?.color else "",
modifier = modifier =
Modifier.clickable { Modifier.clickable {
onCustomizationTap("hair", "color") onCustomizationTap("hair", "color")
} }
) )
OverviewItem( OverviewItem(
stringResource(R.string.avatar_hair_bangs), stringResource(R.string.avatar_hair_bangs),
if (preferences?.hair?.bangs != null && preferences.hair?.bangs != 0) "icon_hair_bangs_" + preferences.hair?.bangs + "_" + preferences.hair?.color else "", if (preferences?.hair?.bangs != null && preferences.hair?.bangs != 0) "icon_hair_bangs_" + preferences.hair?.bangs + "_" + preferences.hair?.color else "",
modifier = modifier =
Modifier.clickable { Modifier.clickable {
onCustomizationTap("hair", "bangs") onCustomizationTap("hair", "bangs")
} }
) )
} }
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
@ -226,33 +245,33 @@ fun AvatarCustomizationOverviewView(
stringResource(R.string.avatar_style), stringResource(R.string.avatar_style),
if (preferences?.hair?.base != null && preferences.hair?.base != 0) "icon_hair_base_" + preferences.hair?.base + "_" + preferences.hair?.color else "", if (preferences?.hair?.base != null && preferences.hair?.base != 0) "icon_hair_base_" + preferences.hair?.base + "_" + preferences.hair?.color else "",
modifier = modifier =
Modifier.clickable { Modifier.clickable {
onCustomizationTap("hair", "base") onCustomizationTap("hair", "base")
} }
) )
OverviewItem( OverviewItem(
stringResource(R.string.avatar_mustache), stringResource(R.string.avatar_mustache),
if (preferences?.hair?.mustache != null && preferences.hair?.mustache != 0) "icon_hair_mustache_" + preferences.hair?.mustache + "_" + preferences.hair?.color else "", if (preferences?.hair?.mustache != null && preferences.hair?.mustache != 0) "icon_hair_mustache_" + preferences.hair?.mustache + "_" + preferences.hair?.color else "",
modifier = modifier =
Modifier.clickable { Modifier.clickable {
onCustomizationTap("hair", "mustache") onCustomizationTap("hair", "mustache")
} }
) )
OverviewItem( OverviewItem(
stringResource(R.string.avatar_beard), stringResource(R.string.avatar_beard),
if (preferences?.hair?.beard != null && preferences.hair?.beard != 0) "icon_hair_beard_" + preferences.hair?.beard + "_" + preferences.hair?.color else "", if (preferences?.hair?.beard != null && preferences.hair?.beard != 0) "icon_hair_beard_" + preferences.hair?.beard + "_" + preferences.hair?.color else "",
modifier = modifier =
Modifier.clickable { Modifier.clickable {
onCustomizationTap("hair", "beard") onCustomizationTap("hair", "beard")
} }
) )
OverviewItem( OverviewItem(
stringResource(R.string.avatar_flower), stringResource(R.string.avatar_flower),
if (preferences?.hair?.flower != null && preferences.hair?.flower != 0) "icon_hair_flower_" + preferences.hair?.flower else "", if (preferences?.hair?.flower != null && preferences.hair?.flower != 0) "icon_hair_flower_" + preferences.hair?.flower else "",
modifier = modifier =
Modifier.clickable { Modifier.clickable {
onCustomizationTap("hair", "flower") onCustomizationTap("hair", "flower")
} }
) )
} }
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) { Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
@ -260,33 +279,33 @@ fun AvatarCustomizationOverviewView(
stringResource(R.string.avatar_wheelchair), stringResource(R.string.avatar_wheelchair),
preferences?.chair?.let { if (it.startsWith("handleless")) "icon_chair_$it" else "icon_$it" }, preferences?.chair?.let { if (it.startsWith("handleless")) "icon_chair_$it" else "icon_$it" },
modifier = modifier =
Modifier.clickable { Modifier.clickable {
onCustomizationTap("chair", null) onCustomizationTap("chair", null)
} }
) )
OverviewItem( OverviewItem(
stringResource(R.string.avatar_background), stringResource(R.string.avatar_background),
preferences?.background.let { "icon_background_$it" }, preferences?.background.let { "icon_background_$it" },
modifier = modifier =
Modifier.clickable { Modifier.clickable {
onCustomizationTap("background", null) onCustomizationTap("background", null)
} }
) )
OverviewItem( OverviewItem(
stringResource(R.string.animal_ears), stringResource(R.string.animal_ears),
outfit?.headAccessory.let { "shop_$it" }, outfit?.headAccessory.let { "shop_$it" },
modifier = modifier =
Modifier.clickable { Modifier.clickable {
onAvatarEquipmentTap("headAccessory", "animal") onAvatarEquipmentTap("headAccessory", "animal")
} }
) )
OverviewItem( OverviewItem(
stringResource(R.string.animal_tail), stringResource(R.string.animal_tail),
outfit?.back.let { "shop_$it" }, outfit?.back.let { "shop_$it" },
modifier = modifier =
Modifier.clickable { Modifier.clickable {
onAvatarEquipmentTap("back", "animal") onAvatarEquipmentTap("back", "animal")
} }
) )
} }
} }
@ -304,7 +323,7 @@ fun EquipmentOverviewItemPreview() {
OverviewItem("Off-Hand", null, isTwoHanded = true) OverviewItem("Off-Hand", null, isTwoHanded = true)
OverviewItem("Armor", null) OverviewItem("Armor", null)
} }
EquipmentOverviewView(null, false, { _, _ -> }) EquipmentOverviewView(null, null, false, { _, _ -> })
AvatarCustomizationOverviewView(null, null, { _, _ -> }, { _, _ -> }) AvatarCustomizationOverviewView(null, null, { _, _ -> }, { _, _ -> })
} }
} }

View file

@ -33,6 +33,9 @@ import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.isTraversalGroup
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
@ -100,7 +103,7 @@ private fun TaskDifficultySelection(
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(6.dp), verticalArrangement = Arrangement.spacedBy(6.dp),
modifier = modifier modifier = modifier.clickable { onSelect(value) }
) { ) {
Box( Box(
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
@ -114,7 +117,6 @@ private fun TaskDifficultySelection(
MaterialTheme.shapes.medium MaterialTheme.shapes.medium
) )
.clip(MaterialTheme.shapes.medium) .clip(MaterialTheme.shapes.medium)
.clickable { onSelect(value) }
) { ) {
this@Column.AnimatedVisibility( this@Column.AnimatedVisibility(
selected, selected,