Skip to content

Commit

Permalink
Merge pull request #6 from apphud/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
ABIvan-Tech authored Mar 14, 2021
2 parents dca6019 + 87b4341 commit 9ebaa5e
Show file tree
Hide file tree
Showing 10 changed files with 316 additions and 24 deletions.
46 changes: 42 additions & 4 deletions sdk/src/androidTest/java/com/apphud/sdk/ApphudServiceTest.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package com.apphud.sdk

import android.util.Log
import com.apphud.sdk.body.AttributionBody
import com.apphud.sdk.body.PurchaseBody
import com.apphud.sdk.body.PurchaseItemBody
import com.apphud.sdk.body.PushBody
import com.apphud.sdk.body.*
import com.apphud.sdk.client.ApiClient
import com.apphud.sdk.client.ApphudService
import com.apphud.sdk.client.HttpUrlConnectionExecutor
Expand Down Expand Up @@ -108,4 +105,45 @@ class ApphudServiceTest {
val response = service.purchase(body)
Log.e("WOW", "send push result: ${response.data.results}")
}

@Test
fun userPropertiesTest() {
val body = UserPropertiesBody(
device_id = deviceId,
properties = listOf(
mapOf(
"set_once" to true,
"kind" to "string",
"value" to "[email protected]",
"name" to "\$email"
),
mapOf(
"kind" to "integer",
"set_once" to false,
"name" to "\$age",
"value" to 31
),
mapOf(
"set_once" to false,
"value" to true,
"name" to "custom_test_property_1",
"kind" to "boolean"
),
mapOf(
"set_once" to false,
"value" to "gay",
"name" to "\$gender",
"kind" to "string"
),
mapOf(
"name" to "custom_email",
"value" to "[email protected]",
"kind" to "string",
"set_once" to true
)
)
)
val response = service.sendUserProperties(body)
Log.e("WOW", "send user properties result: ${response.data.results}")
}
}
4 changes: 3 additions & 1 deletion sdk/src/androidTest/java/com/apphud/sdk/data.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@ internal fun mkRegistrationBody(userId: String, deviceId: String) =
idfa = "22221111",
user_id = userId,
device_id = deviceId,
time_zone = "UTF"
time_zone = "UTF",
is_sandbox = true,
is_new = true
)
57 changes: 50 additions & 7 deletions sdk/src/main/java/com/apphud/sdk/Apphud.kt
Original file line number Diff line number Diff line change
Expand Up @@ -153,30 +153,73 @@ object Apphud {
* Will return **null** if product is not yet fetched from Google Play Billing.
*/
@kotlin.jvm.JvmStatic
fun products(productIdentifier: String): SkuDetails? {
fun product(productIdentifier: String): SkuDetails? {
return ApphudInternal.getSkuDetailsByProductId(productIdentifier)
}

/**
* Purchases product and automatically submit
* @activity: current Activity for use
* @productId: The identifier of the product you wish to purchase
* @block: The closure that will be called when purchase completes.
* @param activity: current Activity for use
* @param productId: The identifier of the product you wish to purchase
* @param block: The closure that will be called when purchase completes.
*/
@kotlin.jvm.JvmStatic
fun purchase(activity: Activity, productId: String, block: (List<Purchase>) -> Unit) =
ApphudInternal.purchase(activity, productId, block)

/**
* Purchases product and automatically submit
* @activity: current Activity for use
* @details: The skuDetails of the product you wish to purchase
* @block: The closure that will be called when purchase completes.
* @param activity current Activity for use
* @param details The skuDetails of the product you wish to purchase
* @param block The closure that will be called when purchase completes.
*/
@kotlin.jvm.JvmStatic
fun purchase(activity: Activity, details: SkuDetails, block: (List<Purchase>) -> Unit) =
ApphudInternal.purchase(activity, details, block)


/**
* Set custom user property.
* Value must be one of: "Int", "Float", "Double", "Boolean", "String" or "null".
*
* Example:
* // use built-in property key
* Apphud.setUserProperty(key: ApphudUserPropertyKey.Email, value: "[email protected]", setOnce: true)
* // use custom property key
* Apphud.setUserProperty(key: ApphudUserPropertyKey.CustomProperty("custom_test_property_1"), value: 0.5)
*
* __Note__: You can use several built-in keys with their value types:
* "ApphudUserPropertyKey.Email": User email. Value must be String.
* "ApphudUserPropertyKey.Name": User name. Value must be String.
* "ApphudUserPropertyKey.Phone": User phone number. Value must be String.
* "ApphudUserPropertyKey.Age": User age. Value must be Int.
* "ApphudUserPropertyKey.Gender": User gender. Value must be one of: "male", "female", "other".
* "ApphudUserPropertyKey.Cohort": User install cohort. Value must be String.
*
* @param key Required. Initialize class with custom string or using built-in keys. See example above.
* @param value Required/Optional. Pass "null" to remove given property from Apphud.
* @param setOnce Optional. Pass "true" to make this property non-updatable.
*/
@kotlin.jvm.JvmStatic
fun setUserProperty(key: ApphudUserPropertyKey, value: Any?, setOnce: Boolean = false) {
ApphudInternal.setUserProperty(key = key, value = value, setOnce = setOnce, increment = false)
}

/**
* Increment custom user property.
* Value must be one of: "Int", "Float", "Double".
*
* Example:
* Apphud.incrementUserProperty(key: ApphudUserPropertyKey.CustomProperty("progress"), by: 0.5)
*
* @param key Required. Use your custom string key or some of built-in keys.
* @param by Required/Optional. You can pass negative value to decrement.
*/
@kotlin.jvm.JvmStatic
fun incrementUserProperty(key: ApphudUserPropertyKey, by: Any) {
ApphudInternal.setUserProperty(key = key, value = by, setOnce = false, increment = true)
}

/**
* Enables debug logs. Better to call this method before SDK initialization.
*/
Expand Down
94 changes: 90 additions & 4 deletions sdk/src/main/java/com/apphud/sdk/ApphudInternal.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@ import android.os.Looper
import com.android.billingclient.api.BillingClient
import com.android.billingclient.api.Purchase
import com.android.billingclient.api.SkuDetails
import com.apphud.sdk.body.AttributionBody
import com.apphud.sdk.body.PurchaseBody
import com.apphud.sdk.body.PurchaseItemBody
import com.apphud.sdk.body.RegistrationBody
import com.apphud.sdk.body.*
import com.apphud.sdk.client.ApphudClient
import com.apphud.sdk.domain.*
import com.apphud.sdk.internal.BillingWrapper
Expand All @@ -28,6 +25,7 @@ internal object ApphudInternal {

private val builder = GsonBuilder()
.setPrettyPrinting()
.serializeNulls()//need this to pass nullable values to JSON and from JSON
.create()
private val parser: Parser = GsonParser(builder)

Expand All @@ -54,6 +52,7 @@ internal object ApphudInternal {
}

private var allowIdentifyUser = true
private var isRegistered = false

internal var userId: UserId? = null
private lateinit var deviceId: DeviceId
Expand All @@ -71,6 +70,20 @@ internal object ApphudInternal {

private var customProductsFetchedBlock : ((List<SkuDetails>) -> Unit)? = null

private val pendingUserProperties = mutableMapOf<String, ApphudUserProperty>()
private val userPropertiesRunnable = Runnable { if(isRegistered) updateUserProperties() }

private var setNeedsToUpdateUserProperties: Boolean = false
set(value) {
field = value
if (value) {
handler.removeCallbacks(userPropertiesRunnable)
handler.postDelayed(userPropertiesRunnable, 1000L)
} else {
handler.removeCallbacks(userPropertiesRunnable)
}
}

private fun loadAdsId() {
if (ApphudUtils.adTracking) {
AdvertisingTask().execute()
Expand Down Expand Up @@ -133,6 +146,7 @@ internal object ApphudInternal {

val body = mkRegistrationBody(userId!!, this.deviceId)
client.registrationUser(body) { customer ->
isRegistered = true
handler.post {
ApphudLog.log("registration registrationUser customer=${customer.toString()}" )
storage.customer = customer
Expand All @@ -144,6 +158,11 @@ internal object ApphudInternal {
ApphudLog.log("registration syncPurchases" )
syncPurchases()
}

if(pendingUserProperties.isNotEmpty() && setNeedsToUpdateUserProperties) {
ApphudLog.log("registration we should update UserProperties" )
updateUserProperties()
}
}
}

Expand Down Expand Up @@ -329,11 +348,76 @@ internal object ApphudInternal {
}
}

internal fun setUserProperty(
key: ApphudUserPropertyKey,
value: Any?,
setOnce: Boolean,
increment: Boolean
) {
val typeString = getType(value)
if (typeString == "unknown") {
val type = value?.let { value::class.java.name } ?: "unknown"
ApphudLog.log("For key '${key.key}' invalid property type: '$type' for 'value'. Must be one of: [Int, Float, Double, Boolean, String or null]")
return
}
if (increment && !(typeString == "integer" || typeString == "float")) {
val type = value?.let { value::class.java.name } ?: "unknown"
ApphudLog.log("For key '${key.key}' invalid increment property type: '$type' for 'value'. Must be one of: [Int, Float or Double]")
return
}

val property = ApphudUserProperty(key = key.key,
value = value,
increment = increment,
setOnce = setOnce,
type = typeString)

pendingUserProperties.run {
remove(property.key)
put(property.key, property)
}
setNeedsToUpdateUserProperties = true
}

private fun updateUserProperties() {
setNeedsToUpdateUserProperties = false
if (pendingUserProperties.isEmpty()) return

val properties = mutableListOf<Map<String, Any?>>()
pendingUserProperties.forEach {
properties.add(it.value.toJSON()!!)
}

val body = UserPropertiesBody(this.deviceId, properties)
client.userProperties(body) { userproperties ->
handler.post {
if (userproperties.success) {
pendingUserProperties.clear()
ApphudLog.log("User Properties successfully updated.")
} else {
ApphudLog.log("User Properties update failed with this errors")
}
}
}
}

private fun getType(value: Any?): String {
return when (value) {
is String -> "string"
is Boolean -> "boolean"
is Float, Double -> "float"
is Int -> "integer"
null -> "null"
else -> "unknown"
}
}

internal fun logout() {
clear()
}

private fun clear() {
isRegistered = false
storage.customer = null
storage.userId = null
storage.deviceId = null
Expand All @@ -343,6 +427,8 @@ internal object ApphudInternal {
skuDetails.clear()
allowIdentifyUser = true
customProductsFetchedBlock = null
pendingUserProperties.clear()
setNeedsToUpdateUserProperties = false
}

private fun fetchProducts() {
Expand Down
36 changes: 36 additions & 0 deletions sdk/src/main/java/com/apphud/sdk/ApphudUserProperty.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.apphud.sdk

internal const val JSON_NAME_NAME = "name"
internal const val JSON_NAME_VALUE = "value"
internal const val JSON_NAME_SET_ONCE = "set_once"
internal const val JSON_NAME_KIND = "kind"
internal const val JSON_NAME_INCREMENT = "increment"

internal data class ApphudUserProperty(
val key: String,
val value: Any?,
val increment: Boolean = false,
val setOnce: Boolean = false,
val type: String = ""
) {

fun toJSON(): MutableMap<String, Any?>? {
if (increment && value == null) {
return null
}

val jsonParamsString: MutableMap<String, Any?> = mutableMapOf(
JSON_NAME_NAME to key,
JSON_NAME_VALUE to if (value !is Float || value !is Double) value else value as Double,
JSON_NAME_SET_ONCE to setOnce
)
if (value != null) {
jsonParamsString[JSON_NAME_KIND] = type
}
if (increment) {
jsonParamsString[JSON_NAME_INCREMENT] = increment
}
return jsonParamsString
}

}
43 changes: 43 additions & 0 deletions sdk/src/main/java/com/apphud/sdk/ApphudUserPropertyKey.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.apphud.sdk

/**
Built-in property keys.
*/
/** User email. Value must be String. */
internal const val ApphudUserPropertyKeyEmail = "\$email"

/** User name. Value must be String. */
internal const val ApphudUserPropertyKeyName = "\$name"

/** User phone number. Value must be String. */
internal const val ApphudUserPropertyKeyPhone = "\$phone"

/** User install cohort. Value must be String. */
internal const val ApphudUserPropertyKeyCohort = "\$cohort"

/** User email. Value must be Int. */
internal const val ApphudUserPropertyKeyAge = "\$age"

/** User email. Value must be one of: "male", "female", "other". */
internal const val ApphudUserPropertyKeyGender = "\$gender"

sealed class ApphudUserPropertyKey(val key: String){
/** User email. Value must be String*/
object Email:ApphudUserPropertyKey(ApphudUserPropertyKeyEmail)
/** User name. Value must be String*/
object Name:ApphudUserPropertyKey(ApphudUserPropertyKeyName)
/** User phone number. Value must be String.*/
object Phone:ApphudUserPropertyKey(ApphudUserPropertyKeyPhone)
/** User age. Value must be Int.*/
object Cohort:ApphudUserPropertyKey(ApphudUserPropertyKeyCohort)
/** User install cohort. Value must be String.*/
object Age:ApphudUserPropertyKey(ApphudUserPropertyKeyAge)
/** User gender. Value must be one of: "male", "female", "other".*/
object Gender:ApphudUserPropertyKey(ApphudUserPropertyKeyGender)
/**
Initialize with custom property key string.
Example:
Apphud.setUserProperty(key = ApphudUserPropertyKey.CustomProperty("custom_prop_1"), value = 0.5)
*/
class CustomProperty(value: String):ApphudUserPropertyKey(value)
}
6 changes: 6 additions & 0 deletions sdk/src/main/java/com/apphud/sdk/body/UserPropertiesBody.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.apphud.sdk.body

data class UserPropertiesBody(
val device_id: String,
val properties: List<Map<String, Any?>>
)
Loading

0 comments on commit 9ebaa5e

Please sign in to comment.