Skip to content

Commit

Permalink
Feature/2.8.0 (#97)
Browse files Browse the repository at this point in the history
* Defer Placements method
* Flush User Properties method
* Does not automatically load permission groups at launch
* Automatic purchases restore at launch
* Can purchase by ProductDetails ID without calling for permission groups
* Improvements for User Properties APIs
---------

Co-authored-by: Vladimir Slatvinski <[email protected]>
  • Loading branch information
ren6 and Vladimir Slatvinski authored Oct 11, 2024
1 parent 749e80b commit 98a6d48
Show file tree
Hide file tree
Showing 25 changed files with 380 additions and 218 deletions.
11 changes: 3 additions & 8 deletions demo_old/src/main/java/com/apphud/demo/ApphudApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import android.util.Log
import androidx.lifecycle.lifecycleScope
import com.apphud.sdk.Apphud
import com.apphud.sdk.ApphudError
import com.apphud.sdk.ApphudUserProperty
import com.apphud.sdk.ApphudUserPropertyKey
import com.apphud.sdk.ApphudUtils
import com.apphud.sdk.client.ApiClient
import com.apphud.sdk.domain.ApphudPaywall
Expand All @@ -20,15 +22,14 @@ import kotlin.coroutines.resume

class ApphudApplication : Application() {
var API_KEY = "YOUR_API_KEY"

companion object {
private lateinit var instance: ApphudApplication

fun applicationContext(): Context {
return instance.applicationContext
}

fun application(): Application {
fun application(): ApphudApplication {
return instance
}
}
Expand All @@ -40,16 +41,10 @@ class ApphudApplication : Application() {
private val applicationScope = CoroutineScope(Dispatchers.Default)

var attempt = 0

override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
ApphudUtils.enableDebugLogs()
}

Apphud.start(this, API_KEY, observerMode = false)
Apphud.collectDeviceIdentifiers()

fetchPlacements()
}

Expand Down
5 changes: 5 additions & 0 deletions demo_old/src/main/java/com/apphud/demo/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.apphud.demo

import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
Expand All @@ -11,7 +12,11 @@ import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController
import com.apphud.demo.databinding.ActivityMainBinding
import com.apphud.sdk.Apphud
import com.google.android.material.navigation.NavigationView
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.RecyclerView
import com.android.billingclient.api.ProductDetails
import com.android.billingclient.api.Purchase
import com.apphud.demo.ApphudApplication
import com.apphud.demo.BuildConfig
import com.apphud.demo.databinding.FragmentCustomerBinding
import com.apphud.sdk.Apphud
Expand Down Expand Up @@ -50,8 +51,7 @@ class CustomerFragment : Fragment() {
binding.appVersion.text = BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")"

binding.btnSync.setOnClickListener {
Apphud.restorePurchases { subscriptions, purchases, error ->
}
Apphud.restorePurchases { _, _, _ -> }
}

paywallsViewModel = ViewModelProvider(this)[PaywallsViewModel::class.java]
Expand Down Expand Up @@ -120,27 +120,11 @@ class CustomerFragment : Fragment() {
}
Apphud.setListener(listener)

getPaywalls()
updateData()

return root
}

private fun getPaywalls() {
Apphud.fetchPlacements { plms, error ->
if (plms.isEmpty() && Apphud.isFallbackMode()) {
val paywall = Apphud.rawPaywalls().firstOrNull()
paywall?.let {
val fallbackPlacement = ApphudPlacement.createCustom("fallback", paywall = it)
Log.d("ApphudLogs", "FALLBACK PLACEMENT: ${fallbackPlacement}")
}
}
if (error != null) {
Log.d("ApphudLogs", "Placements fetch error: ${error.billingErrorTitle()}")
}
}
}

private fun updateData() {
_binding?.isPremiumValue?.text = if (Apphud.hasPremiumAccess()) "Premium" else "No Premium"
lifecycleScope.launch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ class PaywallsViewModel : ViewModel() {
var showPlacements: Boolean = false

suspend fun updateData() {
Log.d("ApphudLogs", "PaywallsViewModel update data")
if (showPlacements) {
items.clear()
val placements = Apphud.rawPlacements()
Expand Down
28 changes: 14 additions & 14 deletions demo_old/src/main/java/com/apphud/demo/ui/groups/GroupsAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@ import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.apphud.demo.R
import com.apphud.sdk.Apphud
import com.apphud.sdk.domain.ApphudGroup
import com.apphud.sdk.domain.ApphudProduct
import com.apphud.sdk.domain.ApphudProductType

class GroupsAdapter(private val groupsViewModel: GroupsViewModel, private val context: Context?) : RecyclerView.Adapter<GroupsAdapter.BaseViewHolder<*>>() {
var selectGroup: ((account: ApphudGroup) -> Unit)? = null
var selectProduct: ((account: ApphudProduct) -> Unit)? = null
var selectProductId: ((account: String) -> Unit)? = null

abstract class BaseViewHolder<T>(itemView: View) : RecyclerView.ViewHolder(itemView) {
abstract fun bind(
Expand All @@ -36,26 +35,27 @@ class GroupsAdapter(private val groupsViewModel: GroupsViewModel, private val co
}
}

inner class ApphudProductViewHolder(itemView: View) : BaseViewHolder<ApphudProduct>(itemView) {
inner class ApphudProductIdViewHolder(itemView: View) : BaseViewHolder<String>(itemView) {
private val productName: TextView = itemView.findViewById(R.id.productName)
private val productId: TextView = itemView.findViewById(R.id.productId)
private val productPrice: TextView = itemView.findViewById(R.id.productPrice)

override fun bind(
item: ApphudProduct,
item: String,
position: Int,
) {
productName.text = item.name
productId.text = item.productId
val productDetails = Apphud.product(item)
productName.text = productDetails?.name
productId.text = productDetails?.productId

if (item.type() == ApphudProductType.SUBS) {
productPrice.text = item.subscriptionOfferDetails()?.firstOrNull()?.pricingPhases?.pricingPhaseList?.firstOrNull()?.formattedPrice ?: ""
if (productDetails?.productType?.lowercase() == "subs") {
productPrice.text = productDetails?.subscriptionOfferDetails?.firstOrNull()?.pricingPhases?.pricingPhaseList?.firstOrNull()?.formattedPrice ?: ""
} else {
productPrice.text = item.oneTimePurchaseOfferDetails()?.formattedPrice ?: ""
productPrice.text = productDetails?.oneTimePurchaseOfferDetails?.formattedPrice ?: ""
}

itemView.setOnClickListener {
selectProduct?.invoke(item)
selectProductId?.invoke(item)
}
}
}
Expand All @@ -80,7 +80,7 @@ class GroupsAdapter(private val groupsViewModel: GroupsViewModel, private val co
val view =
LayoutInflater.from(parent.context)
.inflate(R.layout.list_item_product, parent, false)
ApphudProductViewHolder(view)
ApphudProductIdViewHolder(view)
}
else -> throw IllegalArgumentException("Invalid view type")
}
Expand All @@ -93,15 +93,15 @@ class GroupsAdapter(private val groupsViewModel: GroupsViewModel, private val co
val element = groupsViewModel.items[position]
when (holder) {
is ApphudGroupViewHolder -> holder.bind(element as ApphudGroup, position)
is ApphudProductViewHolder -> holder.bind(element as ApphudProduct, position)
is ApphudProductIdViewHolder -> holder.bind(element as String, position)
else -> throw IllegalArgumentException()
}
}

override fun getItemViewType(position: Int): Int {
return when (groupsViewModel.items[position]) {
is ApphudGroup -> TYPE_GROUP
is ApphudProduct -> TYPE_PRODUCT
is String -> TYPE_PRODUCT
else -> throw IllegalArgumentException("Invalid type of data " + position)
}
}
Expand Down
27 changes: 23 additions & 4 deletions demo_old/src/main/java/com/apphud/demo/ui/groups/GroupsFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,17 @@ import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.RecyclerView
import com.apphud.demo.R
import com.apphud.demo.databinding.FragmentGroupsBinding
import com.apphud.demo.ui.utils.BaseFragment
import com.apphud.sdk.Apphud
import com.apphud.sdk.domain.ApphudPaywall
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class GroupsFragment : Fragment() {
class GroupsFragment : BaseFragment() {
private lateinit var groupsViewModel: GroupsViewModel
private lateinit var viewAdapter: GroupsAdapter
private var _binding: FragmentGroupsBinding? = null
Expand All @@ -31,8 +39,14 @@ class GroupsFragment : Fragment() {
viewAdapter.selectGroup = {
Toast.makeText(activity, it.name, Toast.LENGTH_SHORT).show()
}
viewAdapter.selectProduct = { product ->
// Do nothing here
viewAdapter.selectProductId = { product ->
Apphud.purchase(requireActivity(), product) { result ->
result.error?.let { err ->
Toast.makeText(activity, if (result.userCanceled()) "User Canceled" else err.message, Toast.LENGTH_SHORT).show()
} ?: run {
Toast.makeText(activity, R.string.success, Toast.LENGTH_SHORT).show()
}
}
}

val recyclerView: RecyclerView = binding.groupsList
Expand All @@ -51,7 +65,12 @@ class GroupsFragment : Fragment() {
}

private fun updateData() {
groupsViewModel.updateData()
coroutineScope.launch {
groupsViewModel.updateData()
mainScope.launch {
viewAdapter.notifyDataSetChanged()
}
}
}

override fun onDestroyView() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import com.apphud.sdk.Apphud
class GroupsViewModel : ViewModel() {
var items = mutableListOf<Any>()

fun updateData() {
val list = Apphud.permissionGroups()
suspend fun updateData() {
val list = Apphud.fetchPermissionGroups()
items.clear()

list.forEach {
items.add(it)
it.products?.let { productsList ->
it.productIds().let { productsList ->
items.addAll(productsList)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class ProductsFragment : Fragment() {
if (placementId != null) {
Apphud.placements().firstOrNull { it.identifier == placementId }?.paywall
} else {
Apphud.paywalls().firstOrNull { it.identifier == paywallId }
Apphud.rawPaywalls().firstOrNull { it.identifier == paywallId }
}
return paywall
}
Expand Down
39 changes: 39 additions & 0 deletions demo_old/src/main/java/com/apphud/demo/ui/utils/BaseFragment.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.apphud.demo.ui.utils

import android.util.Log
import androidx.fragment.app.Fragment
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode

class BaseEvent

open class BaseFragment : Fragment() {

val mainScope = CoroutineScope(Dispatchers.Main)
val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
val errorHandler = CoroutineExceptionHandler { _, error ->
error.message?.let {
Log.d("BaseFragment", it)
}
}

override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
}

override fun onStop() {
EventBus.getDefault().unregister(this)
super.onStop()
}

@Subscribe(threadMode = ThreadMode.MAIN)
fun onEvent(event: BaseEvent) {

}
}
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ android.useAndroidX=true
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
sdkVersion=2.7.4
sdkVersion=2.8.0
Loading

0 comments on commit 98a6d48

Please sign in to comment.