mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-04-14 19:56:32 +00:00
Finish 3.4
This commit is contained in:
parent
66af24813a
commit
24aab1ed38
8 changed files with 58 additions and 36 deletions
|
|
@ -155,7 +155,7 @@ android {
|
|||
buildConfigField "String", "TESTING_LEVEL", "\"production\""
|
||||
resConfigs "en", "bg", "de", "en-rGB", "es", "fr", "hr-rHR", "in", "it", "iw", "ja", "ko", "lt", "nl", "pl", "pt-rBR", "pt-rPT", "ru", "tr", "zh", "zh-rTW"
|
||||
|
||||
versionCode 3052
|
||||
versionCode 3059
|
||||
versionName "3.4"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
|
|
|||
|
|
@ -26,32 +26,35 @@ class HabiticaPurchaseVerifier(context: Context, apiClient: ApiClient) : BasePur
|
|||
private val context: Context
|
||||
override fun doVerify(purchases: List<Purchase>, requestListener: RequestListener<List<Purchase>>) {
|
||||
val verifiedPurchases: MutableList<Purchase> = ArrayList(purchases.size)
|
||||
val allPurchases = purchases.toMutableList()
|
||||
for (purchase in purchases) {
|
||||
if (purchasedOrderList.contains(purchase.orderId)) {
|
||||
verifiedPurchases.add(purchase)
|
||||
requestListener.onSuccess(verifiedPurchases)
|
||||
processedPurchase(purchase, allPurchases, verifiedPurchases, requestListener)
|
||||
} else {
|
||||
when {
|
||||
PurchaseTypes.allGemTypes.contains(purchase.sku) -> {
|
||||
val validationRequest = buildValidationRequest(purchase)
|
||||
apiClient.validatePurchase(validationRequest).subscribe({
|
||||
purchasedOrderList.add(purchase.orderId)
|
||||
requestListener.onSuccess(verifiedPurchases)
|
||||
verifiedPurchases.add(purchase)
|
||||
processedPurchase(purchase, allPurchases, verifiedPurchases, requestListener)
|
||||
val giftedID = removeGift(purchase.sku)
|
||||
EventBus.getDefault().post(ConsumablePurchasedEvent(purchase, giftedID))
|
||||
}) { throwable: Throwable ->
|
||||
handleError(throwable, purchase, requestListener, verifiedPurchases)
|
||||
handleError(throwable, purchase, allPurchases, requestListener, verifiedPurchases)
|
||||
}
|
||||
}
|
||||
PurchaseTypes.allSubscriptionNoRenewTypes.contains(purchase.sku) -> {
|
||||
val validationRequest = buildValidationRequest(purchase)
|
||||
apiClient.validateNoRenewSubscription(validationRequest).subscribe({
|
||||
purchasedOrderList.add(purchase.orderId)
|
||||
requestListener.onSuccess(verifiedPurchases)
|
||||
verifiedPurchases.add(purchase)
|
||||
processedPurchase(purchase, allPurchases, verifiedPurchases, requestListener)
|
||||
val giftedID = removeGift(purchase.sku)
|
||||
EventBus.getDefault().post(ConsumablePurchasedEvent(purchase, giftedID))
|
||||
}) { throwable: Throwable ->
|
||||
handleError(throwable, purchase, requestListener, verifiedPurchases)
|
||||
handleError(throwable, purchase, allPurchases, requestListener, verifiedPurchases)
|
||||
}
|
||||
}
|
||||
PurchaseTypes.allSubscriptionTypes.contains(purchase.sku) -> {
|
||||
|
|
@ -63,11 +66,11 @@ class HabiticaPurchaseVerifier(context: Context, apiClient: ApiClient) : BasePur
|
|||
apiClient.validateSubscription(validationRequest).subscribe({
|
||||
purchasedOrderList.add(purchase.orderId)
|
||||
verifiedPurchases.add(purchase)
|
||||
requestListener.onSuccess(verifiedPurchases)
|
||||
processedPurchase(purchase, allPurchases, verifiedPurchases, requestListener)
|
||||
FirebaseAnalytics.getInstance(context).logEvent("user_subscribed", null)
|
||||
EventBus.getDefault().post(UserSubscribedEvent())
|
||||
}) { throwable: Throwable ->
|
||||
handleError(throwable, purchase, requestListener, verifiedPurchases)
|
||||
handleError(throwable, purchase, allPurchases, requestListener, verifiedPurchases)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -79,6 +82,22 @@ class HabiticaPurchaseVerifier(context: Context, apiClient: ApiClient) : BasePur
|
|||
savePendingGifts()
|
||||
}
|
||||
|
||||
private fun processedPurchase(
|
||||
purchase: Purchase,
|
||||
allPurchases: MutableList<Purchase>,
|
||||
verifiedPurchases: MutableList<Purchase>,
|
||||
requestListener: RequestListener<List<Purchase>>
|
||||
) {
|
||||
allPurchases.remove(purchase)
|
||||
if (allPurchases.isEmpty()) {
|
||||
if (verifiedPurchases.isEmpty()) {
|
||||
requestListener.onError(ResponseCodes.ERROR, Exception())
|
||||
} else {
|
||||
requestListener.onSuccess(verifiedPurchases)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildValidationRequest(purchase: Purchase): PurchaseValidationRequest {
|
||||
val validationRequest = PurchaseValidationRequest()
|
||||
validationRequest.sku = purchase.sku
|
||||
|
|
@ -92,13 +111,17 @@ class HabiticaPurchaseVerifier(context: Context, apiClient: ApiClient) : BasePur
|
|||
return validationRequest
|
||||
}
|
||||
|
||||
private fun handleError(throwable: Throwable, purchase: Purchase, requestListener: RequestListener<List<Purchase>>, verifiedPurchases: MutableList<Purchase>) {
|
||||
private fun handleError(throwable: Throwable, purchase: Purchase,
|
||||
allPurchases: MutableList<Purchase>,
|
||||
requestListener: RequestListener<List<Purchase>>,
|
||||
verifiedPurchases: MutableList<Purchase>) {
|
||||
(throwable as? HttpException)?.let { error ->
|
||||
if (error.code() == 401) {
|
||||
val res = apiClient.getErrorResponse(throwable)
|
||||
if (res.message != null && res.message == "RECEIPT_ALREADY_USED") {
|
||||
purchasedOrderList.add(purchase.orderId)
|
||||
requestListener.onSuccess(verifiedPurchases)
|
||||
verifiedPurchases.add(purchase)
|
||||
processedPurchase(purchase, allPurchases, verifiedPurchases, requestListener)
|
||||
EventBus.getDefault().post(ConsumablePurchasedEvent(purchase))
|
||||
removeGift(purchase.sku)
|
||||
return
|
||||
|
|
@ -106,7 +129,7 @@ class HabiticaPurchaseVerifier(context: Context, apiClient: ApiClient) : BasePur
|
|||
}
|
||||
}
|
||||
FirebaseCrashlytics.getInstance().recordException(throwable)
|
||||
requestListener.onError(ResponseCodes.ERROR, Exception())
|
||||
processedPurchase(purchase, allPurchases, verifiedPurchases, requestListener)
|
||||
}
|
||||
|
||||
private fun loadPendingGifts(): MutableMap<String?, String?> {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import com.habitrpg.android.habitica.api.GSonFactoryCreator
|
|||
import com.habitrpg.android.habitica.api.HostConfig
|
||||
import com.habitrpg.android.habitica.api.Server
|
||||
import com.habitrpg.android.habitica.data.ApiClient
|
||||
import com.habitrpg.android.habitica.events.ConsumablePurchasedEvent
|
||||
import com.habitrpg.android.habitica.events.ShowConnectionProblemEvent
|
||||
import com.habitrpg.android.habitica.helpers.NotificationsManager
|
||||
import com.habitrpg.android.habitica.models.*
|
||||
|
|
|
|||
|
|
@ -126,6 +126,9 @@ class AppConfigManager(contentRepository: ContentRepository?) {
|
|||
if (promo == null && remoteConfig.getString("activePromo").isNotBlank()) {
|
||||
promo = getHabiticaPromotionFromKey(remoteConfig.getString("activePromo"), null, null)
|
||||
}
|
||||
if (promo?.isActive != true) {
|
||||
return null
|
||||
}
|
||||
if (promo is HabiticaWebPromotion) {
|
||||
promo.url = surveyURL()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@ enum class PromoType {
|
|||
}
|
||||
|
||||
abstract class HabiticaPromotion {
|
||||
val isActive: Boolean
|
||||
get() {
|
||||
val now = Date()
|
||||
return startDate.before(now) && endDate.after(now)
|
||||
}
|
||||
abstract val identifier: String
|
||||
abstract val promoType: PromoType
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import com.habitrpg.android.habitica.helpers.AppConfigManager
|
|||
import com.habitrpg.android.habitica.helpers.MainNavigationController
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.models.WorldState
|
||||
import com.habitrpg.android.habitica.models.WorldStateEvent
|
||||
import com.habitrpg.android.habitica.models.inventory.Item
|
||||
import com.habitrpg.android.habitica.models.inventory.Quest
|
||||
import com.habitrpg.android.habitica.models.inventory.QuestContent
|
||||
|
|
@ -99,6 +100,7 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
private fun updateQuestDisplay() {
|
||||
val quest = this.quest
|
||||
val questContent = this.questContent
|
||||
return
|
||||
if (quest == null || questContent == null || !quest.active) {
|
||||
binding?.questMenuView?.visibility = View.GONE
|
||||
context?.let {
|
||||
|
|
@ -126,20 +128,6 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
}
|
||||
binding?.questMenuView?.setBackgroundColor(context?.getThemeColor(R.attr.colorPrimaryDark) ?: 0)
|
||||
|
||||
/* Reenable this once the boss art can be displayed correctly.
|
||||
|
||||
val preferences = context?.getSharedPreferences("collapsible_sections", 0)
|
||||
if (preferences?.getBoolean("boss_art_collapsed", false) == true) {
|
||||
questMenuView.hideBossArt()
|
||||
} else {
|
||||
questMenuView.showBossArt()
|
||||
}*/
|
||||
//binding?.questMenuView?.hideBossArt()
|
||||
|
||||
/*getItemWithIdentifier(SIDEBAR_TAVERN)?.let { tavern ->
|
||||
tavern.subtitle = context?.getString(R.string.active_world_boss)
|
||||
adapter.updateItem(tavern)
|
||||
}*/
|
||||
binding?.questMenuView?.setOnClickListener {
|
||||
setSelection(R.id.partyFragment)
|
||||
/*val context = this.context
|
||||
|
|
@ -239,12 +227,12 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
{ pair ->
|
||||
val gearEvent = pair.first.events.firstOrNull { it.gear }
|
||||
createUpdatingJob("seasonal", {
|
||||
gearEvent?.end?.after(Date()) == true || pair.second.isNotEmpty()
|
||||
gearEvent?.isCurrentlyActive == true || pair.second.isNotEmpty()
|
||||
}, {
|
||||
val diff = (gearEvent?.end?.time ?: 0) - Date().time
|
||||
if (diff < (Duration.hours(1).inWholeMilliseconds)) Duration.seconds(1) else Duration.minutes(1)
|
||||
}) {
|
||||
updateSeasonalMenuEntries(pair.first, pair.second)
|
||||
updateSeasonalMenuEntries(gearEvent, pair.second)
|
||||
}
|
||||
},
|
||||
RxErrorHandler.handleEmptyError()
|
||||
|
|
@ -313,7 +301,7 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateSeasonalMenuEntries(worldState: WorldState, items: List<Item>) {
|
||||
private fun updateSeasonalMenuEntries(gearEvent: WorldStateEvent?, items: List<Item>) {
|
||||
val market = getItemWithIdentifier(SIDEBAR_SHOPS_MARKET) ?: return
|
||||
if (items.isNotEmpty() && items.firstOrNull()?.event?.end?.after(Date()) == true) {
|
||||
market.pillText = context?.getString(R.string.something_new)
|
||||
|
|
@ -326,8 +314,7 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
|
||||
val shop = getItemWithIdentifier(SIDEBAR_SHOPS_SEASONAL) ?: return
|
||||
shop.pillText = context?.getString(R.string.open)
|
||||
val gearEvent = worldState.events.firstOrNull { it.gear }
|
||||
if (gearEvent?.end?.after(Date()) == true) {
|
||||
if (gearEvent?.isCurrentlyActive == true) {
|
||||
shop.isVisible = true
|
||||
shop.subtitle = context?.getString(R.string.open_for, gearEvent.end?.getShortRemainingString())
|
||||
} else {
|
||||
|
|
@ -673,13 +660,18 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
promotedItem.pillText = context?.getString(R.string.sale)
|
||||
promotedItem.pillBackground = context?.let { activePromo.pillBackgroundDrawable(it) }
|
||||
createUpdatingJob(activePromo.promoType.name, {
|
||||
activePromo.endDate.after(Date())
|
||||
activePromo.isActive
|
||||
}, {
|
||||
val diff = activePromo.endDate.time - Date().time
|
||||
if (diff < (Duration.hours(1).inWholeMilliseconds)) Duration.seconds(1) else Duration.minutes(1)
|
||||
}) {
|
||||
promotedItem.subtitle = context?.getString(R.string.sale_ends_in, activePromo.endDate.getShortRemainingString())
|
||||
updateItem(promotedItem)
|
||||
if (activePromo.isActive) {
|
||||
promotedItem.subtitle = context?.getString(R.string.sale_ends_in, activePromo.endDate.getShortRemainingString())
|
||||
updateItem(promotedItem)
|
||||
} else {
|
||||
promotedItem.subtitle = null
|
||||
updateItem(promotedItem)
|
||||
}
|
||||
}
|
||||
} ?: run {
|
||||
promoItem.isVisible = false
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ class GuildDetailFragment : BaseFragment<FragmentGuildDetailBinding>() {
|
|||
userIDs.forEach { invites.add(it) }
|
||||
inviteData["usernames"] = invites
|
||||
}
|
||||
viewModel.inviteToGroup(inviteData)
|
||||
viewModel?.inviteToGroup(inviteData)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
We’ve improved more of our notifications so they bring you to the relevant screen when tapped. When customizing your avatar, you’ll be able to see your avatar updating in real time as you select different options. You can also see your current Gems when gifting Gems from your balance now. We’ve fixed a few bugs too, including one where tasks wouldn’t load when the app is left running in the background, the stats widget works again, and another where Dailies wouldn’t filter properly on launch.
|
||||
In this update we’ve fixed some bugs, added more seasonal event support, and made a few quality of life improvements! Sending Guild and Party invites through email should work more reliably. We’ve improved task drag and drop logic when filters are applied to make the order more consistent. Habit streak is now referred to as ‘Habit Counter’ to better reflect the actual behavior. Issues with subscriptions not cancelling fully or getting errors when buying multiple Gem packages should be resolved.
|
||||
|
|
|
|||
Loading…
Reference in a new issue