new shop headers

This commit is contained in:
Phillip Thelen 2024-06-06 10:44:33 +02:00
parent bb5d971211
commit 413c3bbc7b
21 changed files with 230 additions and 39 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 267 B

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="#55311D"/>
<corners android:radius="8dp"/>
<stroke android:width="3dp" android:color="#EA8C31"/>
</shape>

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 B

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0">
<path android:fillColor="@color/white" android:pathData="M7,10l5,5 5,-5z"/>
</vector>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="#B36213"/>
<corners android:radius="8dp"/>
<stroke android:width="3dp" android:color="#EA8C31"/>
</shape>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="@color/gray_500"/>
<corners android:bottomLeftRadius="6dp" android:bottomRightRadius="6dp"/>
</shape>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="@color/yellow_50"/>
<corners android:bottomLeftRadius="6dp" android:bottomRightRadius="6dp"/>
</shape>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="@color/brand_200"/>
<corners android:bottomLeftRadius="6dp" android:bottomRightRadius="6dp"/>
</shape>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="@color/brand_300"/>
<corners android:bottomLeftRadius="6dp" android:bottomRightRadius="6dp"/>
</shape>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="@color/maroon_50"/>
<corners android:bottomLeftRadius="6dp" android:bottomRightRadius="6dp"/>
</shape>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="@color/blue_10"/>
<corners android:bottomLeftRadius="6dp" android:bottomRightRadius="6dp"/>
</shape>

View file

@ -4,46 +4,97 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:orientation="vertical"
android:layout_marginHorizontal="12dp">
<LinearLayout
android:id="@+id/header_layout"
android:id="@+id/header_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp">
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@drawable/shop_category_header_background"
android:layout_marginBottom="@dimen/spacing_large">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="12dp"
android:paddingVertical="9dp"
android:gravity="center_vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/shop_header_sparkle" />
<TextView
android:id="@+id/label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
style="@style/Caption2"
style="@style/Body1"
tools:text="Section Header"
android:textColor="?textColorSecondary" />
android:gravity="center"
android:textColor="@color/white" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/shop_header_sparkle" />
</LinearLayout>
<View
android:layout_width="wrap_content"
android:layout_height="3dp"
android:background="#EA8C31" />
<TextView
android:id="@+id/switches_in_label"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?textColorTintedSecondary"
android:gravity="center_vertical"
android:textColor="@color/white"
android:background="@drawable/shop_header_time_background"
android:layout_marginHorizontal="3dp"
android:layout_marginBottom="3dp"
android:paddingVertical="6dp"
android:gravity="center"
android:layout_gravity="center_vertical"
style="@style/Caption2"
style="@style/Body1"
tools:text="Switches in X"
android:visibility="gone"
tools:visibility="visible" />
<FrameLayout
android:id="@+id/class_selection_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:layout_marginBottom="3dp"
android:layout_marginHorizontal="3dp"
android:paddingStart="12dp"
android:paddingEnd="3dp"
android:paddingVertical="6dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/class_name_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
style="@style/Body1"
/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/ic_arrow_drop_down_10dp"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
<Spinner
android:id="@+id/class_selection_spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_height="10dp"
android:visibility="gone"
android:layout_gravity="bottom"
tools:visibility="gone" />
</FrameLayout>
</LinearLayout>
<TextView

View file

@ -54,7 +54,7 @@
<item name="popupMenuStyle">@style/PopupTheme</item>
<item name="actionOverflowMenuStyle">@style/PopupTheme</item>
<item name="android:popupMenuStyle">@style/PopupTheme</item>
<item name="android:textColorLink">@color/brand_400</item>
<item name="android:textColorLink">@color/text_brand_neon</item>
<item name="alertDialogTheme">@style/AlertDialogTheme</item>
<item name="android:windowTranslucentStatus">false</item>

View file

@ -7,12 +7,12 @@ import android.text.style.ForegroundColorSpan
import androidx.core.content.ContextCompat
import com.habitrpg.android.habitica.R
class EmptyShopCategory(val categoryIdentifier: String, context: Context?) {
class EmptyShopCategory(val categoryIdentifier: String, val shopIdentifier: String?, context: Context?) {
val title: String = context?.getString(R.string.you_own_all_items) ?: ""
val description: Spannable
init {
val stringId = when (categoryIdentifier) {
"backgrounds" -> R.string.try_on_next_month
"backgrounds" -> if (shopIdentifier == Shop.CUSTOMIZATIONS) R.string.try_on_next_month else R.string.try_on_customize
"color" -> R.string.try_on_next_season
"skin" -> R.string.try_on_next_season
"mystery_sets" -> R.string.try_on_equipment

View file

@ -5,7 +5,10 @@ import android.text.method.LinkMovementMethod
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.view.isVisible
import androidx.core.view.marginTop
import androidx.recyclerview.widget.RecyclerView.ViewHolder
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.ShopArmoireGearBinding
@ -26,6 +29,7 @@ import com.habitrpg.android.habitica.ui.viewHolders.SectionViewHolder
import com.habitrpg.android.habitica.ui.viewHolders.ShopItemViewHolder
import com.habitrpg.android.habitica.ui.views.getTranslatedClassName
import com.habitrpg.android.habitica.ui.views.insufficientCurrency.InsufficientGemsDialog
import com.habitrpg.common.habitica.extensions.dpToPx
import com.habitrpg.common.habitica.extensions.fromHtml
import com.habitrpg.common.habitica.extensions.loadImage
import com.habitrpg.common.habitica.helpers.MainNavigationController
@ -93,7 +97,7 @@ class ShopRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter<Vi
for (category in shop.categories) {
items.add(category)
if (category.items.isEmpty()) {
items.add(EmptyShopCategory(category.identifier, context))
items.add(EmptyShopCategory(category.identifier, shopIdentifier, context))
} else {
for (item in category.items) {
item.categoryIdentifier = category.identifier
@ -144,6 +148,11 @@ class ShopRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter<Vi
val sectionHolder = holder as? SectionViewHolder ?: return
sectionHolder.bind(obj.text)
sectionHolder.bind(obj.endDate)
(sectionHolder.headerContainer.layoutParams as? LinearLayout.LayoutParams)?.topMargin = if (position > 1) {
40.dpToPx(context)
} else {
16.dpToPx(context)
}
if (gearCategories.contains(obj)) {
context?.let { context ->
val adapter =
@ -160,6 +169,7 @@ class ShopRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter<Vi
gearCategories[holder.selectedItem].identifier
}
}
sectionHolder.setSelectedClass(selectedGearCategory)
if (user?.stats?.habitClass != obj.identifier && obj.identifier != "none") {
if (user?.hasClass == true) {
sectionHolder.switchClassButton?.setOnClickListener {
@ -199,13 +209,20 @@ class ShopRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapter<Vi
} else {
sectionHolder.switchClassButton?.visibility = View.GONE
}
sectionHolder.notesView?.visibility = View.VISIBLE
sectionHolder.notesView?.text = context.getString(
R.string.class_gear_disclaimer
)
} else {
sectionHolder.switchClassButton?.visibility = View.GONE
sectionHolder.notesView?.visibility = View.GONE
}
}
sectionHolder.divider?.visibility = View.VISIBLE
} else {
sectionHolder.spinnerAdapter = null
sectionHolder.notesView?.visibility = View.GONE
sectionHolder.divider?.isVisible = obj.endDate != null
}
}

View file

@ -8,10 +8,16 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import androidx.compose.foundation.background
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.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -22,8 +28,10 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.map
import com.habitrpg.android.habitica.R
@ -34,6 +42,7 @@ import com.habitrpg.android.habitica.interactors.ShareAvatarUseCase
import com.habitrpg.android.habitica.models.inventory.Equipment
import com.habitrpg.android.habitica.ui.activities.BaseActivity
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
import com.habitrpg.android.habitica.ui.helpers.ToolbarColorHelper
import com.habitrpg.android.habitica.ui.theme.colors
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
import com.habitrpg.android.habitica.ui.views.SegmentedControl
@ -42,6 +51,8 @@ import com.habitrpg.android.habitica.ui.views.equipment.EquipmentOverviewView
import com.habitrpg.common.habitica.helpers.MainNavigationController
import com.habitrpg.common.habitica.helpers.launchCatching
import com.habitrpg.common.habitica.theme.HabiticaTheme
import com.habitrpg.common.habitica.views.ComposableAvatarView
import com.habitrpg.shared.habitica.models.Avatar
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.firstOrNull
import javax.inject.Inject
@ -78,27 +89,48 @@ open class AvatarOverviewFragment :
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
hidesToolbar = true
val view = super.onCreateView(inflater, container, savedInstanceState)
binding?.composeView?.apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
HabiticaTheme {
AvatarOverviewView(
userViewModel,
showCustomization,
!showCustomization,
battleGearWeapon.value?.twoHanded == true,
costumeWeapon.value?.twoHanded == true,
{ type, category ->
displayCustomizationFragment(type, category)
},
{ type, category ->
displayAvatarEquipmentFragment(type, category)
},
{ type, equipped, isCostume ->
displayEquipmentFragment(type, equipped, isCostume)
},
)
val avatar by userViewModel.user.observeAsState()
Column {
Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.background(colorResource(R.color.window_background))) {
ComposableAvatarView(
avatar = avatar,
configManager = appConfigManager,
modifier =
Modifier
.padding(vertical = 24.dp)
.size(140.dp, 147.dp),
)
Box(
Modifier
.background(colorResource(R.color.content_background), RoundedCornerShape(topStart = 22.dp, topEnd = 22.dp))
.fillMaxWidth()
.height(22.dp),
)
}
AvatarOverviewView(
userViewModel,
showCustomization,
!showCustomization,
battleGearWeapon.value?.twoHanded == true,
costumeWeapon.value?.twoHanded == true,
{ type, category ->
displayCustomizationFragment(type, category)
},
{ type, category ->
displayAvatarEquipmentFragment(type, category)
},
{ type, equipped, isCostume ->
displayEquipmentFragment(type, equipped, isCostume)
},
)
}
}
}
}
@ -169,6 +201,12 @@ open class AvatarOverviewFragment :
) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.menu_share_avatar, menu)
mainActivity?.toolbar?.let {
val color = ContextCompat.getColor(requireContext(), R.color.window_background)
ToolbarColorHelper.colorizeToolbar(it, mainActivity, backgroundColor = color)
requireActivity().window.statusBarColor = color
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {

View file

@ -6,9 +6,12 @@ import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.Spinner
import android.widget.TextView
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.extensions.getImpreciseRemainingString
@ -19,6 +22,7 @@ import com.habitrpg.android.habitica.ui.views.CurrencyView
import java.util.Date
class SectionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val headerContainer: LinearLayout = itemView.findViewById(R.id.header_container)
private val label: TextView = itemView.findViewById(R.id.label)
private val switchesInLabel: TextView? = itemView.findViewById(R.id.switches_in_label)
private val selectionSpinner: Spinner? = itemView.findViewById(R.id.class_selection_spinner)
@ -28,6 +32,9 @@ class SectionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val switchClassCurrency: CurrencyView? = itemView.findViewById(R.id.change_class_currency_view)
internal val notesView: TextView? = itemView.findViewById(R.id.headerNotesView)
private val countPill: TextView? = itemView.findViewById(R.id.count_pill)
val divider: View? = itemView.findViewById(R.id.divider)
val classSelectionButton: FrameLayout? = itemView.findViewById(R.id.class_selection_button)
val classSelectionLabel: TextView? = itemView.findViewById(R.id.class_name_label)
var context: Context = itemView.context
var spinnerSelectionChanged: (() -> Unit)? = null
@ -35,6 +42,9 @@ class SectionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
constructor(parent: ViewGroup) : this(parent.inflate(R.layout.customization_section_header))
init {
classSelectionButton?.setOnClickListener {
selectionSpinner?.performClick()
}
selectionSpinner?.onItemSelectedListener =
object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {
@ -99,11 +109,40 @@ class SectionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
}
fun setSelectedClass(selectedGearCategory: String) {
var textColor = R.color.white
when (selectedGearCategory) {
"warrior" -> {
classSelectionButton?.background = AppCompatResources.getDrawable(context, R.drawable.shop_header_warrior_background)
classSelectionLabel?.text = context.getString(R.string.warrior)
}
"wizard" -> {
classSelectionButton?.background = AppCompatResources.getDrawable(context, R.drawable.shop_header_wizard_background)
classSelectionLabel?.text = context.getString(R.string.mage)
}
"healer" -> {
classSelectionButton?.background = AppCompatResources.getDrawable(context, R.drawable.shop_header_healer_background)
classSelectionLabel?.text = context.getString(R.string.healer)
textColor = R.color.yellow_1
}
"rogue" -> {
classSelectionButton?.background = AppCompatResources.getDrawable(context, R.drawable.shop_header_rogue_background)
classSelectionLabel?.text = context.getString(R.string.rogue)
}
else -> {
classSelectionButton?.background = AppCompatResources.getDrawable(context, R.drawable.shop_header_else_background)
classSelectionLabel?.text = context.getString(R.string.classless)
textColor = R.color.gray_100
}
}
classSelectionLabel?.setTextColor(ContextCompat.getColor(context, textColor))
}
var spinnerAdapter: ArrayAdapter<CharSequence>? = null
set(value) {
field = value
selectionSpinner?.adapter = field
selectionSpinner?.visibility = if (value != null) View.VISIBLE else View.GONE
classSelectionButton?.visibility = if (value != null) View.VISIBLE else View.GONE
}
var selectedItem: Int = 0

View file

@ -120,7 +120,7 @@ class ShopItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), Vi
binding.itemDetailIndicator.layoutParams = layoutParams
}
binding.priceLabel.isLocked = item.locked || !canBuy
binding.priceLabel.isLocked = item.locked || (!canBuy && item.currency == "gold")
}
override fun onClick(view: View) {

View file

@ -1,2 +1,2 @@
NAME=4.3.7
CODE=7841
CODE=7861