From 24aab1ed38bac17b78f26e6c6c670b69d8cdb498 Mon Sep 17 00:00:00 2001 From: Phillip Thelen Date: Wed, 6 Oct 2021 13:53:49 +0200 Subject: [PATCH] Finish 3.4 --- Habitica/build.gradle | 2 +- .../habitica/HabiticaPurchaseVerifier.kt | 43 ++++++++++++++----- .../data/implementation/ApiClientImpl.kt | 1 - .../habitica/helpers/AppConfigManager.kt | 3 ++ .../models/promotions/HabiticaPromotion.kt | 5 +++ .../ui/fragments/NavigationDrawerFragment.kt | 36 ++++++---------- .../social/guilds/GuildDetailFragment.kt | 2 +- fastlane/changelog.txt | 2 +- 8 files changed, 58 insertions(+), 36 deletions(-) diff --git a/Habitica/build.gradle b/Habitica/build.gradle index c924bba11..ae8b8bdab 100644 --- a/Habitica/build.gradle +++ b/Habitica/build.gradle @@ -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" diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/HabiticaPurchaseVerifier.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/HabiticaPurchaseVerifier.kt index f96c84d9d..52e234277 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/HabiticaPurchaseVerifier.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/HabiticaPurchaseVerifier.kt @@ -26,32 +26,35 @@ class HabiticaPurchaseVerifier(context: Context, apiClient: ApiClient) : BasePur private val context: Context override fun doVerify(purchases: List, requestListener: RequestListener>) { val verifiedPurchases: MutableList = 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, + verifiedPurchases: MutableList, + requestListener: RequestListener> + ) { + 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>, verifiedPurchases: MutableList) { + private fun handleError(throwable: Throwable, purchase: Purchase, + allPurchases: MutableList, + requestListener: RequestListener>, + verifiedPurchases: MutableList) { (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 { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt index 38c17e637..942b46c64 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt @@ -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.* diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AppConfigManager.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AppConfigManager.kt index 8965115dd..8f0195f5d 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AppConfigManager.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AppConfigManager.kt @@ -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() } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/promotions/HabiticaPromotion.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/promotions/HabiticaPromotion.kt index 0c145f841..149d4362c 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/promotions/HabiticaPromotion.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/promotions/HabiticaPromotion.kt @@ -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 diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/NavigationDrawerFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/NavigationDrawerFragment.kt index 452d898e5..8b9228c87 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/NavigationDrawerFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/NavigationDrawerFragment.kt @@ -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) { + private fun updateSeasonalMenuEntries(gearEvent: WorldStateEvent?, items: List) { 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 diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/guilds/GuildDetailFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/guilds/GuildDetailFragment.kt index c0b64f33c..093539c71 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/guilds/GuildDetailFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/guilds/GuildDetailFragment.kt @@ -129,7 +129,7 @@ class GuildDetailFragment : BaseFragment() { userIDs.forEach { invites.add(it) } inviteData["usernames"] = invites } - viewModel.inviteToGroup(inviteData) + viewModel?.inviteToGroup(inviteData) } } diff --git a/fastlane/changelog.txt b/fastlane/changelog.txt index a0fb0b394..d06852bc4 100644 --- a/fastlane/changelog.txt +++ b/fastlane/changelog.txt @@ -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.