Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: refactor context provider #191

Merged
merged 8 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 64 additions & 45 deletions Confidence/api/Confidence.api
Original file line number Diff line number Diff line change
@@ -1,26 +1,3 @@
public final class com/spotify/confidence/AndroidLifecycleEventProducer : android/app/Application$ActivityLifecycleCallbacks, androidx/lifecycle/DefaultLifecycleObserver, com/spotify/confidence/EventProducer {
public static final field Companion Lcom/spotify/confidence/AndroidLifecycleEventProducer$Companion;
public fun <init> (Landroid/app/Application;Z)V
public fun contextChanges ()Lkotlinx/coroutines/flow/Flow;
public fun events ()Lkotlinx/coroutines/flow/Flow;
public fun onActivityCreated (Landroid/app/Activity;Landroid/os/Bundle;)V
public fun onActivityDestroyed (Landroid/app/Activity;)V
public fun onActivityPaused (Landroid/app/Activity;)V
public fun onActivityResumed (Landroid/app/Activity;)V
public fun onActivitySaveInstanceState (Landroid/app/Activity;Landroid/os/Bundle;)V
public fun onActivityStarted (Landroid/app/Activity;)V
public fun onActivityStopped (Landroid/app/Activity;)V
public fun onCreate (Landroidx/lifecycle/LifecycleOwner;)V
public fun stop ()V
}

public final class com/spotify/confidence/AndroidLifecycleEventProducer$Companion {
}

public final class com/spotify/confidence/AndroidLifecycleEventProducerKt {
public static final fun getReferrer (Landroid/app/Activity;)Landroid/net/Uri;
}

public final class com/spotify/confidence/BuildConfig {
public static final field BUILD_TYPE Ljava/lang/String;
public static final field DEBUG Z
Expand All @@ -46,7 +23,7 @@ public final class com/spotify/confidence/Confidence : com/spotify/confidence/Co
public final fun putContext (Ljava/util/Map;Ljava/util/List;)V
public fun removeContext (Ljava/lang/String;)V
public fun stop ()V
public fun track (Lcom/spotify/confidence/EventProducer;)V
public fun track (Lcom/spotify/confidence/Producer;)V
public fun track (Ljava/lang/String;Ljava/util/Map;)V
public synthetic fun withContext (Ljava/util/Map;)Lcom/spotify/confidence/Contextual;
public fun withContext (Ljava/util/Map;)Lcom/spotify/confidence/EventSender;
Expand All @@ -56,6 +33,28 @@ public abstract interface class com/spotify/confidence/ConfidenceContextProvider
public abstract fun getContext ()Ljava/util/Map;
}

public final class com/spotify/confidence/ConfidenceDeviceInfoContextProducer : com/spotify/confidence/ContextProducer {
public static final field APP_BUILD_CONTEXT_KEY Ljava/lang/String;
public static final field APP_NAMESPACE_CONTEXT_KEY Ljava/lang/String;
public static final field APP_VERSION_CONTEXT_KEY Ljava/lang/String;
public static final field Companion Lcom/spotify/confidence/ConfidenceDeviceInfoContextProducer$Companion;
public static final field DEVICE_BRAND_CONTEXT_KEY Ljava/lang/String;
public static final field DEVICE_MANUFACTURER_CONTEXT_KEY Ljava/lang/String;
public static final field DEVICE_MODEL_CONTEXT_KEY Ljava/lang/String;
public static final field DEVICE_TYPE_CONTEXT_KEY Ljava/lang/String;
public static final field LOCALE_CONTEXT_KEY Ljava/lang/String;
public static final field OS_NAME_CONTEXT_KEY Ljava/lang/String;
public static final field OS_VERSION_CONTEXT_KEY Ljava/lang/String;
public static final field PREFERRED_LANGUAGES_CONTEXT_KEY Ljava/lang/String;
public fun <init> (Landroid/content/Context;ZZZZ)V
public synthetic fun <init> (Landroid/content/Context;ZZZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun stop ()V
public fun updates ()Lkotlinx/coroutines/flow/Flow;
}

public final class com/spotify/confidence/ConfidenceDeviceInfoContextProducer$Companion {
}

public final class com/spotify/confidence/ConfidenceError {
public fun <init> ()V
}
Expand Down Expand Up @@ -286,6 +285,7 @@ public final class com/spotify/confidence/ConfidenceValue$List$Companion {
public final class com/spotify/confidence/ConfidenceValue$Null : com/spotify/confidence/ConfidenceValue {
public static final field INSTANCE Lcom/spotify/confidence/ConfidenceValue$Null;
public final fun serializer ()Lkotlinx/serialization/KSerializer;
public fun toString ()Ljava/lang/String;
}

public final class com/spotify/confidence/ConfidenceValue$String : com/spotify/confidence/ConfidenceValue {
Expand Down Expand Up @@ -379,6 +379,9 @@ public final class com/spotify/confidence/ConfidenceValueKt {
public static final fun toNetworkJson (Lcom/spotify/confidence/ConfidenceValue$Struct;)Ljava/lang/String;
}

public abstract interface class com/spotify/confidence/ContextProducer : com/spotify/confidence/Producer {
}

public abstract interface class com/spotify/confidence/Contextual : com/spotify/confidence/ConfidenceContextProvider {
public abstract fun putContext (Ljava/lang/String;Lcom/spotify/confidence/ConfidenceValue;)V
public abstract fun putContext (Ljava/util/Map;)V
Expand Down Expand Up @@ -406,32 +409,13 @@ public final class com/spotify/confidence/Evaluation {
public fun toString ()Ljava/lang/String;
}

public final class com/spotify/confidence/Event {
public fun <init> (Ljava/lang/String;Ljava/util/Map;Z)V
public synthetic fun <init> (Ljava/lang/String;Ljava/util/Map;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/util/Map;
public final fun component3 ()Z
public final fun copy (Ljava/lang/String;Ljava/util/Map;Z)Lcom/spotify/confidence/Event;
public static synthetic fun copy$default (Lcom/spotify/confidence/Event;Ljava/lang/String;Ljava/util/Map;ZILjava/lang/Object;)Lcom/spotify/confidence/Event;
public fun equals (Ljava/lang/Object;)Z
public final fun getData ()Ljava/util/Map;
public final fun getName ()Ljava/lang/String;
public final fun getShouldFlush ()Z
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public abstract interface class com/spotify/confidence/EventProducer {
public abstract fun contextChanges ()Lkotlinx/coroutines/flow/Flow;
public abstract fun events ()Lkotlinx/coroutines/flow/Flow;
public abstract fun stop ()V
public abstract interface class com/spotify/confidence/EventProducer : com/spotify/confidence/Producer {
}

public abstract interface class com/spotify/confidence/EventSender : com/spotify/confidence/Contextual {
public abstract fun flush ()V
public abstract fun stop ()V
public abstract fun track (Lcom/spotify/confidence/EventProducer;)V
public abstract fun track (Lcom/spotify/confidence/Producer;)V
public abstract fun track (Ljava/lang/String;Ljava/util/Map;)V
public abstract fun withContext (Ljava/util/Map;)Lcom/spotify/confidence/EventSender;
}
Expand Down Expand Up @@ -490,6 +474,11 @@ public final class com/spotify/confidence/LoggingLevel : java/lang/Enum {
public static fun values ()[Lcom/spotify/confidence/LoggingLevel;
}

public abstract interface class com/spotify/confidence/Producer {
public abstract fun stop ()V
public abstract fun updates ()Lkotlinx/coroutines/flow/Flow;
}

public abstract interface class com/spotify/confidence/ProviderCache {
public abstract fun get ()Lcom/spotify/confidence/FlagResolution;
public abstract fun refresh (Lcom/spotify/confidence/FlagResolution;)V
Expand Down Expand Up @@ -536,6 +525,36 @@ public final class com/spotify/confidence/Result$Success : com/spotify/confidenc
public fun toString ()Ljava/lang/String;
}

public abstract interface class com/spotify/confidence/Update {
}

public final class com/spotify/confidence/Update$ContextUpdate : com/spotify/confidence/Update {
public fun <init> (Ljava/util/Map;)V
public final fun component1 ()Ljava/util/Map;
public final fun copy (Ljava/util/Map;)Lcom/spotify/confidence/Update$ContextUpdate;
public static synthetic fun copy$default (Lcom/spotify/confidence/Update$ContextUpdate;Ljava/util/Map;ILjava/lang/Object;)Lcom/spotify/confidence/Update$ContextUpdate;
public fun equals (Ljava/lang/Object;)Z
public final fun getContext ()Ljava/util/Map;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class com/spotify/confidence/Update$Event : com/spotify/confidence/Update {
public fun <init> (Ljava/lang/String;Ljava/util/Map;Z)V
public synthetic fun <init> (Ljava/lang/String;Ljava/util/Map;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/util/Map;
public final fun component3 ()Z
public final fun copy (Ljava/lang/String;Ljava/util/Map;Z)Lcom/spotify/confidence/Update$Event;
public static synthetic fun copy$default (Lcom/spotify/confidence/Update$Event;Ljava/lang/String;Ljava/util/Map;ZILjava/lang/Object;)Lcom/spotify/confidence/Update$Event;
public fun equals (Ljava/lang/Object;)Z
public final fun getData ()Ljava/util/Map;
public final fun getName ()Ljava/lang/String;
public final fun getShouldFlush ()Z
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class com/spotify/confidence/apply/ApplyInstance {
public static final field Companion Lcom/spotify/confidence/apply/ApplyInstance$Companion;
public synthetic fun <init> (ILjava/util/Date;Lcom/spotify/confidence/apply/EventStatus;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
Expand Down
1 change: 0 additions & 1 deletion Confidence/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ dependencies {
implementation(
"org.jetbrains.kotlinx:kotlinx-serialization-json:${Versions.kotlinxSerialization}"
)
implementation("androidx.lifecycle:lifecycle-process:2.6.1")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${Versions.coroutines}")
testImplementation("junit:junit:${Versions.junit}")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:${Versions.coroutines}")
Expand Down
36 changes: 17 additions & 19 deletions Confidence/src/main/java/com/spotify/confidence/Confidence.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class Confidence internal constructor(
private var currentFetchJob: Job? = null

private val coroutineScope = CoroutineScope(SupervisorJob() + dispatcher)
private val eventProducers: MutableList<EventProducer> = mutableListOf()
private val producers: MutableList<Producer> = mutableListOf()

private val flagApplier = FlagApplierWithRetries(
client = flagApplierClient,
Expand Down Expand Up @@ -276,31 +276,29 @@ class Confidence internal constructor(
activate()
}

override fun track(eventProducer: EventProducer) {
override fun track(producer: Producer) {
coroutineScope.launch {
eventProducer
.events()
.collect { event ->
eventSenderEngine.emit(
event.name,
event.data,
getContext()
)
if (event.shouldFlush) {
eventSenderEngine.flush()
producer.updates().collect { update ->
when (update) {
is Update.Event -> {
eventSenderEngine.emit(
update.name,
update.data,
getContext()
)
if (update.shouldFlush) {
eventSenderEngine.flush()
}
}
is Update.ContextUpdate -> putContext(update.context)
}
nicklasl marked this conversation as resolved.
Show resolved Hide resolved
}
producers.add(producer)
}

coroutineScope.launch {
eventProducer.contextChanges()
.collect(this@Confidence::putContext)
}
eventProducers.add(eventProducer)
}

override fun stop() {
for (producer in eventProducers) {
for (producer in producers) {
producer.stop()
}
if (parent == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package com.spotify.confidence

import android.content.Context
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import java.util.Locale

class ConfidenceDeviceInfoContextProducer(
applicationContext: Context,
withAppInfo: Boolean = false,
withDeviceInfo: Boolean = false,
withOsInfo: Boolean = false,
withLocale: Boolean = false
) : ContextProducer {
private val contextFlow = MutableStateFlow<Map<String, ConfidenceValue>>(mapOf())
private val packageInfo: PackageInfo? = try {
@Suppress("DEPRECATION")
applicationContext.packageManager.getPackageInfo(applicationContext.packageName, 0)
} catch (e: PackageManager.NameNotFoundException) {
Log.w(DebugLogger.TAG, "Failed to get package info", e)
nicklasl marked this conversation as resolved.
Show resolved Hide resolved
null
}

init {
val context = mutableMapOf<String, ConfidenceValue>()
if (withAppInfo) {
val currentVersion = ConfidenceValue.String(packageInfo?.versionName ?: "")
val currentBuild = ConfidenceValue.String(packageInfo?.getVersionCodeAsString() ?: "")
val bundleId = ConfidenceValue.String(applicationContext.packageName)
context["app"] = ConfidenceValue.Struct(
mapOf(
APP_VERSION_CONTEXT_KEY to currentVersion,
APP_BUILD_CONTEXT_KEY to currentBuild,
APP_NAMESPACE_CONTEXT_KEY to bundleId
)
)
}

if (withDeviceInfo) {
context["device"] = ConfidenceValue.Struct(
mapOf(
DEVICE_MANUFACTURER_CONTEXT_KEY to ConfidenceValue.String(Build.MANUFACTURER),
DEVICE_BRAND_CONTEXT_KEY to ConfidenceValue.String(Build.BRAND),
DEVICE_MODEL_CONTEXT_KEY to ConfidenceValue.String(Build.MODEL),
DEVICE_TYPE_CONTEXT_KEY to ConfidenceValue.String("android")
)
)
}

if (withOsInfo) {
context["os"] = ConfidenceValue.Struct(
mapOf(
OS_NAME_CONTEXT_KEY to ConfidenceValue.String("android"),
OS_VERSION_CONTEXT_KEY to ConfidenceValue.Double(Build.VERSION.SDK_INT.toDouble())
)
)
}

if (withLocale) {
val preferredLang = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val locales = applicationContext.resources.configuration.locales
(0 until locales.size()).map { locales.get(it).toString() }
} else {
listOf(Locale.getDefault().toString())
}
val localeIdentifier = Locale.getDefault().toString()
val localeInfo = mapOf(
LOCALE_CONTEXT_KEY to ConfidenceValue.String(localeIdentifier),
PREFERRED_LANGUAGES_CONTEXT_KEY to ConfidenceValue.List(preferredLang.map(ConfidenceValue::String))
)
// these are on the top level
context += localeInfo
}
contextFlow.value = context
}

// override fun contextChanges(): Flow<Map<String, ConfidenceValue>> = contextFlow
override fun updates(): Flow<Update> = contextFlow.map { Update.ContextUpdate(it) }

override fun stop() {}

companion object {
const val APP_VERSION_CONTEXT_KEY = "version"
const val APP_BUILD_CONTEXT_KEY = "build"
const val APP_NAMESPACE_CONTEXT_KEY = "namespace"
const val DEVICE_MANUFACTURER_CONTEXT_KEY = "manufacturer"
const val DEVICE_BRAND_CONTEXT_KEY = "brand"
const val DEVICE_MODEL_CONTEXT_KEY = "model"
const val DEVICE_TYPE_CONTEXT_KEY = "type"
const val OS_NAME_CONTEXT_KEY = "name"
const val OS_VERSION_CONTEXT_KEY = "version"
const val LOCALE_CONTEXT_KEY = "locale"
const val PREFERRED_LANGUAGES_CONTEXT_KEY = "preferred_languages"
}
}

private fun PackageInfo.getVersionCodeAsString(): String =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
this.longVersionCode.toString()
} else {
@Suppress("DEPRECATION")
this.versionCode.toString()
}
Loading
Loading