Finish 3.4

This commit is contained in:
Phillip Thelen 2021-10-06 13:53:49 +02:00
parent 66af24813a
commit 24aab1ed38
8 changed files with 58 additions and 36 deletions

View file

@ -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"

View file

@ -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?> {

View file

@ -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.*

View file

@ -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()
}

View file

@ -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

View file

@ -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

View file

@ -129,7 +129,7 @@ class GuildDetailFragment : BaseFragment<FragmentGuildDetailBinding>() {
userIDs.forEach { invites.add(it) }
inviteData["usernames"] = invites
}
viewModel.inviteToGroup(inviteData)
viewModel?.inviteToGroup(inviteData)
}
}

View file

@ -1 +1 @@
Weve improved more of our notifications so they bring you to the relevant screen when tapped. When customizing your avatar, youll 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. Weve fixed a few bugs too, including one where tasks wouldnt load when the app is left running in the background, the stats widget works again, and another where Dailies wouldnt filter properly on launch.
In this update weve 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. Weve 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.