diff --git a/Habitica/res/drawable/circle_gray_700.xml b/Habitica/res/drawable/circle_gray_700.xml
new file mode 100644
index 000000000..a6defe225
--- /dev/null
+++ b/Habitica/res/drawable/circle_gray_700.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/activity_armoire.xml b/Habitica/res/layout/activity_armoire.xml
index 43eacc7bc..bbcaf28b5 100644
--- a/Habitica/res/layout/activity_armoire.xml
+++ b/Habitica/res/layout/activity_armoire.xml
@@ -13,52 +13,85 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:gravity="center_horizontal"
- android:paddingTop="19dp">
-
-
-
-
-
-
-
-
+
+
-
+ android:orientation="vertical"
+ android:gravity="center_horizontal">
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Habitica/res/layout/can_hatch_item.xml b/Habitica/res/layout/can_hatch_item.xml
new file mode 100644
index 000000000..602982584
--- /dev/null
+++ b/Habitica/res/layout/can_hatch_item.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Habitica/res/layout/purchase_gem_view.xml b/Habitica/res/layout/purchase_gem_view.xml
index b83800c33..8635a9a37 100644
--- a/Habitica/res/layout/purchase_gem_view.xml
+++ b/Habitica/res/layout/purchase_gem_view.xml
@@ -17,6 +17,10 @@
android:contentDescription="@string/gems"
android:layout_marginTop="20dp"/>
+
+
+
@@ -101,7 +102,7 @@
-
+
@@ -160,4 +161,11 @@
+
+
+
+
+
+
+
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/Animations.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/Animations.kt
new file mode 100644
index 000000000..b92c764c7
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/Animations.kt
@@ -0,0 +1,86 @@
+package com.habitrpg.android.habitica.helpers
+
+import android.view.View
+import android.view.ViewAnimationUtils
+import android.view.animation.AccelerateDecelerateInterpolator
+import android.view.animation.AlphaAnimation
+import android.view.animation.Animation
+import android.view.animation.Animation.INFINITE
+import android.view.animation.Animation.RELATIVE_TO_SELF
+import android.view.animation.Animation.REVERSE
+import android.view.animation.AnimationSet
+import android.view.animation.LinearInterpolator
+import android.view.animation.RotateAnimation
+import android.view.animation.TranslateAnimation
+import androidx.core.animation.doOnEnd
+import kotlin.random.Random
+
+object Animations {
+ private fun randomFloat(min: Float, max: Float): Float {
+ return min + Random.nextFloat() * (max - min)
+ }
+
+ fun bobbingAnimation(amount: Float = 8f): Animation {
+ val anim = TranslateAnimation(0f, 0f, -amount, amount)
+ anim.duration = 2500
+ anim.interpolator = AccelerateDecelerateInterpolator()
+ anim.repeatCount = INFINITE
+ anim.repeatMode = REVERSE
+ return anim
+ }
+
+ fun negativeShakeAnimation(intensity: Float = 1f): Animation {
+ val anim = AnimationSet(true)
+ anim.interpolator = LinearInterpolator()
+
+ val translate = TranslateAnimation(randomFloat(-2f * intensity, 0f), randomFloat(0f, 2f * intensity), randomFloat(-1f * intensity, 0f), randomFloat(0f, 1f * intensity))
+ translate.duration = 70
+ translate.repeatCount = 5
+ translate.repeatMode = REVERSE
+ anim.addAnimation(translate)
+
+ val rotate = RotateAnimation(randomFloat(-0.4f * intensity, 0f), randomFloat(0f, 0.4f * intensity), RELATIVE_TO_SELF, 0.5f, RELATIVE_TO_SELF, 0.5f)
+ rotate.duration = 70
+ rotate.repeatCount = 5
+ rotate.repeatMode = REVERSE
+ anim.addAnimation(rotate)
+
+ return anim
+ }
+
+ fun circularReveal(view: View, duration: Long = 300) {
+ val cx = view.width / 2
+ val cy = view.height / 2
+ val finalRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat()
+ val anim = ViewAnimationUtils.createCircularReveal(view, cx, cy, 0f, finalRadius)
+ anim.duration = duration
+ view.visibility = View.VISIBLE
+ anim.start()
+ }
+
+ fun circularHide(view: View) {
+ val cx = view.width / 2
+ val cy = view.height / 2
+ val initialRadius = Math.hypot(cx.toDouble(), cy.toDouble()).toFloat()
+ val anim = ViewAnimationUtils.createCircularReveal(view, cx, cy, initialRadius, 0f)
+ anim.doOnEnd {
+ view.visibility = View.INVISIBLE
+ }
+ anim.start()
+ }
+
+ fun fadeInAnimation(duration: Long = 300): Animation {
+ val anim = AlphaAnimation(0f, 1f)
+ anim.interpolator = AccelerateDecelerateInterpolator()
+ anim.fillBefore = true
+ anim.fillAfter = true
+ anim.duration = duration
+ return anim
+ }
+
+ fun fadeOutAnimation(): Animation {
+ val anim = AlphaAnimation(1f, 0f)
+ anim.interpolator = AccelerateDecelerateInterpolator()
+ return anim
+ }
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/AvatarWithBarsViewModel.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/AvatarWithBarsViewModel.kt
index bf6a1a385..ac9b8dee7 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/AvatarWithBarsViewModel.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/AvatarWithBarsViewModel.kt
@@ -9,6 +9,7 @@ import androidx.core.os.bundleOf
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.UserRepository
import com.habitrpg.android.habitica.databinding.AvatarWithBarsBinding
+import com.habitrpg.android.habitica.helpers.Animations
import com.habitrpg.android.habitica.helpers.HealthFormatter
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.models.Avatar
@@ -96,6 +97,9 @@ class AvatarWithBarsViewModel(
if (valueMax != 0) {
cachedMaxHealth = valueMax
}
+ if (binding.hpBar.currentValue > value) {
+ binding.hpBar.progressBar.startAnimation(Animations.negativeShakeAnimation())
+ }
binding.hpBar.set(HealthFormatter.format(value.toDouble()), cachedMaxHealth.toDouble())
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ArmoireActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ArmoireActivity.kt
index e00125826..cebcdc579 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ArmoireActivity.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ArmoireActivity.kt
@@ -6,22 +6,26 @@ import android.view.Gravity
import android.view.View
import android.view.animation.AccelerateInterpolator
import android.widget.FrameLayout
+import android.widget.RelativeLayout
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.InventoryRepository
import com.habitrpg.android.habitica.databinding.ActivityArmoireBinding
+import com.habitrpg.android.habitica.extensions.dpToPx
import com.habitrpg.android.habitica.extensions.observeOnce
import com.habitrpg.android.habitica.helpers.AdHandler
import com.habitrpg.android.habitica.helpers.AdType
+import com.habitrpg.android.habitica.helpers.Animations
import com.habitrpg.android.habitica.helpers.AppConfigManager
import com.habitrpg.android.habitica.helpers.RxErrorHandler
-import com.habitrpg.android.habitica.ui.helpers.loadImage
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
import com.habitrpg.android.habitica.ui.views.ads.AdButton
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaBottomSheetDialog
import com.plattysoft.leonids.ParticleSystem
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
import javax.inject.Inject
class ArmoireActivity: BaseActivity() {
@@ -86,7 +90,7 @@ class ArmoireActivity: BaseActivity() {
}, RxErrorHandler.handleEmptyError()))
}
handler.prepare {
- if (it && binding.adButton.state == AdButton.State.EMPTY) {
+ if (it && binding.adButton.state == AdButton.State.LOADING) {
binding.adButton.state = AdButton.State.READY
} else if (!it) {
binding.adButton.visibility = View.INVISIBLE
@@ -141,6 +145,34 @@ class ArmoireActivity: BaseActivity() {
},
500
)
+
+
+ binding.iconView.startAnimation(Animations.bobbingAnimation())
+ binding.titleView.alpha = 0f
+ binding.subtitleView.alpha = 0f
+
+ lifecycleScope.launch {
+ delay(100)
+ if (binding.iconWrapper.isAttachedToWindow) {
+ Animations.circularReveal(binding.iconWrapper, 400)
+ }
+ binding.leftSparkView.startAnimating()
+ binding.rightSparkView.startAnimating()
+ }
+
+ binding.titleView.animate().apply {
+ alpha(1f)
+ duration = 400
+ startDelay = 600
+ start()
+ }
+ binding.subtitleView.animate().apply {
+ alpha(1f)
+ duration = 400
+ startDelay = 900
+ start()
+ }
+
hasAnimatedChanges = true
}
@@ -171,8 +203,9 @@ class ArmoireActivity: BaseActivity() {
binding.iconView.loadImage("Pet_Food_$key")
}
else -> {
- binding.subtitleView.text = getString(R.string.armoireExp, value)
+ binding.subtitleView.text = getString(R.string.armoireExp_new, value)
binding.iconView.setImageResource(R.drawable.armoire_experience)
+ binding.iconView.layoutParams = RelativeLayout.LayoutParams(108.dpToPx(this), 122.dpToPx(this))
}
}
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/PetDetailRecyclerAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/PetDetailRecyclerAdapter.kt
index 690e26b64..018d96d6d 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/PetDetailRecyclerAdapter.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/inventory/PetDetailRecyclerAdapter.kt
@@ -1,6 +1,11 @@
package com.habitrpg.android.habitica.ui.adapter.inventory
+import android.view.View
import android.view.ViewGroup
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.databinding.CanHatchItemBinding
+import com.habitrpg.android.habitica.extensions.inflate
+import com.habitrpg.android.habitica.helpers.Animations
import com.habitrpg.android.habitica.models.inventory.Animal
import com.habitrpg.android.habitica.models.inventory.Egg
import com.habitrpg.android.habitica.models.inventory.Food
@@ -11,8 +16,10 @@ import com.habitrpg.android.habitica.models.inventory.StableSection
import com.habitrpg.android.habitica.models.user.OwnedItem
import com.habitrpg.android.habitica.models.user.OwnedMount
import com.habitrpg.android.habitica.models.user.OwnedPet
+import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
import com.habitrpg.android.habitica.ui.viewHolders.PetViewHolder
import com.habitrpg.android.habitica.ui.viewHolders.SectionViewHolder
+import com.habitrpg.android.habitica.ui.views.dialogs.PetSuggestHatchDialog
import io.reactivex.rxjava3.core.BackpressureStrategy
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.subjects.PublishSubject
@@ -66,6 +73,7 @@ class PetDetailRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapt
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder =
when (viewType) {
1 -> SectionViewHolder(parent)
+ 2 -> CanHatchViewHolder(parent, animalIngredientsRetriever)
else -> PetViewHolder(parent, equipEvents, feedEvents, animalIngredientsRetriever)
}
@@ -78,23 +86,50 @@ class PetDetailRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapt
(holder as? SectionViewHolder)?.bind(obj)
}
is Pet -> {
- (holder as? PetViewHolder)?.bind(
- obj,
- ownedPets?.get(obj.key ?: "")?.trained ?: 0,
- eggCount(obj),
- potionCount(obj),
- canRaiseToMount(obj),
- ownsSaddles,
- ownedItems?.get(obj.animal + "-eggs") != null,
- ownedItems?.get(obj.color + "-hatchingPotions") != null,
- ownedMounts?.containsKey(obj.key) == true,
- currentPet
- )
+ val trained = ownedPets?.get(obj.key ?: "")?.trained ?: 0
+ val eggCount = eggCount(obj)
+ val potionCount = potionCount(obj)
+ if (trained <= 0 && eggCount > 0 && potionCount > 0) {
+ (holder as? CanHatchViewHolder)?.bind(
+ obj,
+ eggCount,
+ potionCount,
+ ownedItems?.get(obj.animal + "-eggs") != null,
+ ownedItems?.get(obj.color + "-hatchingPotions") != null,
+ ownedMounts?.containsKey(obj.key) == true,
+ )
+ } else {
+ (holder as? PetViewHolder)?.bind(
+ obj,
+ trained,
+ eggCount,
+ potionCount,
+ canRaiseToMount(obj),
+ ownsSaddles,
+ ownedItems?.get(obj.animal + "-eggs") != null,
+ ownedItems?.get(obj.color + "-hatchingPotions") != null,
+ ownedMounts?.containsKey(obj.key) == true,
+ currentPet
+ )
+ }
+
}
}
}
- override fun getItemViewType(position: Int): Int = if (itemList.size > position && itemList[position] is StableSection) 1 else 2
+ override fun getItemViewType(position: Int): Int {
+ if (itemList.size <= position) return 3
+ return if (itemList[position] is StableSection) {
+ 1
+ } else {
+ val pet = itemList[position] as Pet
+ if (ownedPets?.get(pet.key ?: "")?.trained ?: 0 <= 0 && eggCount(pet) > 0 && potionCount(pet) > 0) {
+ 2
+ } else {
+ 3
+ }
+ }
+ }
override fun getItemCount(): Int = itemList.size
@@ -118,4 +153,68 @@ class PetDetailRecyclerAdapter : androidx.recyclerview.widget.RecyclerView.Adapt
ownsSaddles = if (ownedItems.containsKey("Saddle-food")) (ownedItems["Saddle-food"]?.numberOwned ?: 0) > 0 else false
notifyDataSetChanged()
}
+
+ class CanHatchViewHolder(
+ parent: ViewGroup,
+ private val ingredientsReceiver: ((Animal, ((Pair) -> Unit)) -> Unit)?
+ ) : androidx.recyclerview.widget.RecyclerView.ViewHolder(parent.inflate(R.layout.can_hatch_item)),
+ View.OnClickListener {
+ private var binding = CanHatchItemBinding.bind(itemView)
+
+ private var hasMount: Boolean = false
+ private var hasUnlockedPotion: Boolean = false
+ private var hasUnlockedEgg: Boolean = false
+ private var eggCount: Int = 0
+ private var potionCount: Int = 0
+ private var animal: Pet? = null
+
+ init {
+ itemView.setOnClickListener(this)
+ }
+
+ fun bind(
+ item: Pet,
+ eggCount: Int,
+ potionCount: Int,
+ hasUnlockedEgg: Boolean,
+ hasUnlockedPotion: Boolean,
+ hasMount: Boolean,
+ ) {
+ this.animal = item
+ this.eggCount = eggCount
+ this.potionCount = potionCount
+ this.hasUnlockedEgg = hasUnlockedEgg
+ this.hasUnlockedPotion = hasUnlockedPotion
+ this.hasMount = hasMount
+
+ DataBindingUtils.loadImage(binding.eggView, "Pet_Egg_${item.animal}")
+ DataBindingUtils.loadImage(
+ binding.hatchingPotionView,
+ "Pet_HatchingPotion_${item.color}"
+ )
+
+ binding.eggView.startAnimation(Animations.bobbingAnimation(4f))
+ binding.hatchingPotionView.startAnimation(Animations.bobbingAnimation(-4f))
+ }
+
+ override fun onClick(p0: View?) {
+ val context = itemView.context
+ val dialog = PetSuggestHatchDialog(context)
+ animal?.let {
+ ingredientsReceiver?.invoke(it) { ingredients ->
+ dialog.configure(
+ it,
+ ingredients.first,
+ ingredients.second,
+ eggCount,
+ potionCount,
+ hasUnlockedEgg,
+ hasUnlockedPotion,
+ hasMount
+ )
+ dialog.show()
+ }
+ }
+ }
+ }
}
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/PetViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/PetViewHolder.kt
index 40ac5124e..c5567aa55 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/PetViewHolder.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/PetViewHolder.kt
@@ -89,17 +89,6 @@ class PetViewHolder(
} else {
binding.trainedProgressBar.visibility = View.GONE
binding.imageView.alpha = 0.2f
- if (canHatch) {
- binding.imageView.visibility = View.GONE
- binding.itemWrapper.visibility = View.VISIBLE
- binding.checkmarkView.visibility = View.VISIBLE
- itemView.setBackgroundResource(R.drawable.layout_rounded_bg_window_tint_border)
- DataBindingUtils.loadImage(binding.eggView, "Pet_Egg_${item.animal}")
- DataBindingUtils.loadImage(
- binding.hatchingPotionView,
- "Pet_HatchingPotion_${item.color}"
- )
- }
}
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaProgressBar.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaProgressBar.kt
index ba7597093..fd496f576 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaProgressBar.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/HabiticaProgressBar.kt
@@ -45,8 +45,20 @@ class HabiticaProgressBar(context: Context, attrs: AttributeSet?) : FrameLayout(
}
}
- private var currentValue: Double = 0.0
- private var maxValue: Double = 0.0
+ var currentValue: Double = 0.0
+ set(value) {
+ if (field != value) {
+ field = value
+ updateBar()
+ }
+ }
+ var maxValue: Double = 0.0
+ set(value) {
+ if (field != value) {
+ field = value
+ updateBar()
+ }
+ }
var pendingValue: Double = 0.0
set(value) {
@@ -97,16 +109,6 @@ class HabiticaProgressBar(context: Context, attrs: AttributeSet?) : FrameLayout(
updateBar()
}
- fun setCurrentValue(value: Double) {
- currentValue = value
- updateBar()
- }
-
- fun setMaxValue(value: Double) {
- maxValue = value
- updateBar()
- }
-
private fun setLayoutWeight(view: View, weight: Double) {
view.clearAnimation()
val layout = view.layoutParams as? LinearLayout.LayoutParams
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/SparkView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/SparkView.kt
new file mode 100644
index 000000000..05f51bcfb
--- /dev/null
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/SparkView.kt
@@ -0,0 +1,104 @@
+package com.habitrpg.android.habitica.ui.views
+
+import android.animation.ObjectAnimator
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.util.AttributeSet
+import android.view.View
+import android.view.animation.AccelerateDecelerateInterpolator
+import android.view.animation.Animation
+import androidx.core.content.ContextCompat
+import com.habitrpg.android.habitica.R
+import com.habitrpg.android.habitica.extensions.dpToPx
+import kotlin.math.min
+
+class SparkView @JvmOverloads constructor(
+ context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
+) : View(context, attrs, defStyleAttr) {
+ private var spacing: Float = 0f
+ set(value) {
+ field = value
+ invalidate()
+ }
+ private var paint: Paint = Paint()
+
+ var thickness = 3.dpToPx(context)
+ var length = 6.dpToPx(context)
+ var maxSpacing = 5.dpToPx(context)
+ var animationDuration = 2500L
+ var color: Int
+ get() {
+ return paint.color
+ }
+ set(value) {
+ paint.color = value
+ }
+
+ init {
+ spacing = maxSpacing.toFloat()
+ context.theme?.obtainStyledAttributes(attrs, R.styleable.SparkView, 0, 0)?.let {
+ thickness = it.getDimensionPixelSize(R.styleable.SparkView_thickness, 3.dpToPx(context))
+ length = it.getDimensionPixelSize(R.styleable.SparkView_length, 6.dpToPx(context))
+ maxSpacing = it.getDimensionPixelSize(R.styleable.SparkView_maxSpacing, 5.dpToPx(context))
+ animationDuration = it.getInt(R.styleable.SparkView_duration, 2500).toLong()
+ color = it.getInt(R.styleable.SparkView_color, ContextCompat.getColor(context, R.color.white))
+ }
+
+ paint.style = Paint.Style.FILL
+ }
+
+ fun startAnimating() {
+ val anim = ObjectAnimator.ofFloat(thickness.toFloat(), maxSpacing.toFloat())
+ anim.addUpdateListener {
+ spacing = it.animatedValue as Float
+ }
+ anim.interpolator = AccelerateDecelerateInterpolator()
+ anim.repeatCount = Animation.INFINITE
+ anim.repeatMode = ValueAnimator.REVERSE
+ anim.duration = animationDuration
+ anim.start()
+ }
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ val widthSize = MeasureSpec.getSize(widthMeasureSpec)
+ val heightSize = MeasureSpec.getSize(heightMeasureSpec)
+ val desiredSize = (length * 2 + maxSpacing)
+
+ val width = when (MeasureSpec.getMode(widthMeasureSpec)) {
+ MeasureSpec.EXACTLY -> widthSize
+ MeasureSpec.AT_MOST -> min(desiredSize, widthSize)
+ else -> desiredSize
+ }
+
+ val height = when (MeasureSpec.getMode(heightMeasureSpec)) {
+ MeasureSpec.EXACTLY -> heightSize
+ MeasureSpec.AT_MOST -> min(desiredSize, heightSize)
+ else -> desiredSize
+ }
+
+ setMeasuredDimension(width, height)
+ }
+
+ override fun onDraw(canvas: Canvas?) {
+ super.onDraw(canvas)
+ val thisCanvas = canvas ?: return
+ val centerHorizontal = width / 2f
+ val centerVertical = height / 2f
+ val offset = (maxSpacing - spacing)/2
+ drawHorizontal(thisCanvas, offset, centerVertical)
+ drawHorizontal(thisCanvas, width - length.toFloat() - offset, centerVertical)
+
+ drawVertical(thisCanvas, centerHorizontal, offset)
+ drawVertical(thisCanvas, centerVertical, height - length.toFloat() - offset)
+ }
+
+ private fun drawVertical(canvas: Canvas, x: Float, y: Float) {
+ canvas.drawRoundRect(x-(thickness/2), y, x+(thickness/2), y+length, thickness/2f, thickness/2f, paint)
+ }
+
+ private fun drawHorizontal(canvas: Canvas, x: Float, y: Float) {
+ canvas.drawRoundRect(x, y-(thickness/2), x+length, y+(thickness/2), thickness/2f, thickness/2f, paint)
+ }
+}
\ No newline at end of file
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/ValueBar.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/ValueBar.kt
index d970d12e6..fc652a3a9 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/ValueBar.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/ValueBar.kt
@@ -28,8 +28,19 @@ class ValueBar(context: Context, attrs: AttributeSet?) : FrameLayout(context, at
private var binding: ValueBarBinding = ValueBarBinding.inflate(context.layoutInflater, this, true)
private val formatter = NumberFormat.getInstance()
- private var currentValue: Double = 0.0
- private var maxValue: Double = 0.0
+ val progressBar: HabiticaProgressBar
+ get() = binding.progressBar
+
+ var currentValue: Double
+ get() = binding.progressBar.currentValue
+ set(value) {
+ binding.progressBar.currentValue = value
+ }
+ var maxValue: Double
+ get() = binding.progressBar.maxValue
+ set(value) {
+ binding.progressBar.maxValue = value
+ }
var barForegroundColor: Int
get() = binding.progressBar.barForegroundColor
@@ -169,7 +180,7 @@ class ValueBar(context: Context, attrs: AttributeSet?) : FrameLayout(context, at
var animationDelay = 0L
fun set(value: Double, valueMax: Double) {
- if (currentValue != value || maxValue != valueMax) {
+ if (binding.progressBar.currentValue != value || maxValue != valueMax) {
if (animationDuration == 0L || binding.valueTextView.text.isEmpty()) {
currentValue = value
} else {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/ads/AdButton.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/ads/AdButton.kt
index 6f2ad0075..a67ef1973 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/ads/AdButton.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/ads/AdButton.kt
@@ -25,7 +25,7 @@ class AdButton @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : LinearLayout(context, attrs) {
- var state: State = State.EMPTY
+ var state: State = State.LOADING
set(value) {
field = value
updateViews()
@@ -61,7 +61,7 @@ class AdButton @JvmOverloads constructor(
binding.currencyView.setTextColor(ContextCompat.getColor(context, R.color.white))
binding.currencyView.value = 0.0
gravity = Gravity.CENTER
- state = State.EMPTY
+ state = State.LOADING
}
private fun updateViews() {
diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/social/QuestMenuView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/social/QuestMenuView.kt
index 803aa3918..4b7225af6 100644
--- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/social/QuestMenuView.kt
+++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/social/QuestMenuView.kt
@@ -36,30 +36,22 @@ class QuestMenuView : LinearLayout {
binding.rageIconView.setImageBitmap(HabiticaIconsHelper.imageOfRage())
binding.pendingDamageIconView.setImageBitmap(HabiticaIconsHelper.imageOfDamage())
-
- /*binding.closeButton.setOnClickListener {
- hideBossArt()
- val preferences = context.getSharedPreferences("collapsible_sections", 0)
- preferences?.edit {
- putBoolean("boss_art_collapsed", true)
- }
- }*/
}
fun configure(quest: Quest) {
- binding.healthBarView.setCurrentValue(quest.progress?.hp ?: 0.0)
- binding.rageBarView.setCurrentValue(quest.progress?.rage ?: 0.0)
+ binding.healthBarView.currentValue = quest.progress?.hp ?: 0.0
+ binding.rageBarView.currentValue = quest.progress?.rage ?: 0.0
}
fun configure(questContent: QuestContent) {
this.questContent = questContent
- binding.healthBarView.setMaxValue(questContent.boss?.hp?.toDouble() ?: 0.0)
+ binding.healthBarView.maxValue = questContent.boss?.hp?.toDouble() ?: 0.0
binding.bossNameView.text = questContent.boss?.name
binding.typeTextView.text = context.getString(R.string.boss_quest)
if (questContent.boss?.hasRage == true) {
binding.rageView.visibility = View.VISIBLE
- binding.rageBarView.setMaxValue(questContent.boss?.rage?.value ?: 0.0)
+ binding.rageBarView.maxValue = questContent.boss?.rage?.value ?: 0.0
} else {
binding.rageView.visibility = View.GONE
}
@@ -74,9 +66,7 @@ class QuestMenuView : LinearLayout {
binding.topView.setBackgroundColor(questContent?.colors?.mediumColor ?: 0)
binding.bossNameView.gravity = Gravity.START
binding.bossNameView.layoutParams = LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1F)
- // binding.bossArtView.visibility = View.GONE
binding.typeTextView.setTextColor(questContent?.colors?.extraLightColor ?: 0)
- // binding.closeButton.visibility = View.GONE
}
fun showBossArt() {
@@ -84,8 +74,6 @@ class QuestMenuView : LinearLayout {
binding.topView.setBackgroundColor(ContextCompat.getColor(context, R.color.transparent))
binding.bossNameView.gravity = Gravity.END
binding.bossNameView.layoutParams = LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
- // binding.bossArtView.visibility = View.VISIBLE
binding.typeTextView.setTextColor(ContextCompat.getColor(context, R.color.white))
- // binding.closeButton.visibility = View.VISIBLE
}
}