Skip to content

[PLUGINS] add konfeature plugin #21

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

Merged
merged 2 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ dependencies {
api(androidx.room.runtime)
api(rmr.flipper)
api(rmr.itemsadapter.viewbinding)
api(libs.konfeature)
api(stack.accompanist.themeadapter.core)
api(stack.accompanist.themeadapter.material)
api(stack.kotlinx.coroutines.android)
Expand Down
5 changes: 5 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[versions]
konfeature = "0.1.0"

[libraries]
konfeature = { module = "com.redmadrobot.konfeature:konfeature", version.ref = "konfeature" }
1 change: 1 addition & 0 deletions no-op/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@ dependencies {
implementation(stack.okhttp)
implementation(androidx.appcompat)
implementation(rmr.flipper)
implementation(libs.konfeature)
implementation(stack.kotlinx.coroutines.android)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.redmadrobot.debug.plugin.konfeature

import android.content.Context
import com.redmadrobot.konfeature.source.FeatureValueSource
import com.redmadrobot.konfeature.source.Interceptor


public class KonfeatureDebugPanelInterceptor(context: Context) : Interceptor {

override val name: String = "NoopDebugPanelInterceptor"

override fun intercept(valueSource: FeatureValueSource, key: String, value: Any): Any? = null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.redmadrobot.debug.plugin.konfeature

import com.redmadrobot.konfeature.Konfeature

public class KonfeaturePlugin(
private val debugPanelInterceptor: KonfeatureDebugPanelInterceptor,
private val konfeature: Konfeature,
)
1 change: 1 addition & 0 deletions plugins/konfeature/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
54 changes: 54 additions & 0 deletions plugins/konfeature/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
plugins {
id(Plugins.Android.libraryPlagin)
kotlin(Plugins.Kotlin.androidPlugin)
kotlin(Plugins.Kotlin.kapt)
id("convention-publish")
}

description = "Plugin for konfeature library integration"

android {
compileSdk = Project.COMPILE_SDK
lint.targetSdk = Project.TARGET_SDK

defaultConfig {
minSdk = Project.MIN_SDK

consumerProguardFile("consumer-rules.pro")
}

buildTypes {
getByName(Project.BuildTypes.release) {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile(Project.Proguard.androidOptimizedRules),
Project.Proguard.projectRules
)
}
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs += "-Xexplicit-api=strict"
}

buildFeatures {
compose = true
}

composeOptions {
kotlinCompilerExtensionVersion = androidx.versions.compose.compiler.get()
}
namespace = "com.redmadrobot.debug.plugin.konfeature"
}

dependencies {
implementation(project(":core"))
implementation(project(":common"))
implementation(androidx.lifecycle.runtime)
}
Empty file.
3 changes: 3 additions & 0 deletions plugins/konfeature/library.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
lib_name = plugin-flipper
lib_vcs=https://github.com/RedMadRobot/debug-panel-android.git
lib_issue_tracker=https://github.com/RedMadRobot/debug-panel-android/issues
21 changes: 21 additions & 0 deletions plugins/konfeature/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.kts.kts.kts.kts.kts.kts.kts.kts.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package com.redmadrobot.debug.plugin.konfeature

import android.content.Context
import android.content.SharedPreferences
import androidx.core.content.edit
import com.redmadrobot.debug.plugin.konfeature.util.JsonConverter
import com.redmadrobot.konfeature.source.FeatureValueSource
import com.redmadrobot.konfeature.source.Interceptor
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import org.json.JSONObject
import timber.log.Timber

public class KonfeatureDebugPanelInterceptor(context: Context) : Interceptor {

private val preferences by lazy {
context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE)
}

private val _valuesFlow = MutableStateFlow(emptyMap<String, Any>())

internal val valuesFlow = _valuesFlow.asStateFlow()

private val mutex = Mutex()

override val name: String = "DebugPanelInterceptor"

init {
CoroutineScope(Dispatchers.IO).launch { fetchValues() }
}

override fun intercept(valueSource: FeatureValueSource, key: String, value: Any): Any? {
return _valuesFlow.value[key]
?.let { convertTypeIfNeeded(it, value) }
?.takeIf { it != value }
}

private suspend fun fetchValues() {
_valuesFlow.value = withContext(Dispatchers.IO) {
mutex.withLock { preferences.fetchValues() }
}
}

private fun convertTypeIfNeeded(debugValue: Any, value: Any): Any {
var result = when {
debugValue is Int -> debugValue.toLong()
debugValue is Float -> debugValue.toDouble()
else -> debugValue
}
if (result is Long && value is Double) {
result = result.toDouble()
}
return result
}

internal suspend fun setValue(key: String, value: Any) {
_valuesFlow.update { it + (key to value) }
updateValues(_valuesFlow.value)
}

internal suspend fun resetValue(key: String) {
_valuesFlow.update { it - key }
updateValues(_valuesFlow.value)
}

internal suspend fun resetAll() {
_valuesFlow.value = emptyMap<String, Any>()
updateValues(_valuesFlow.value)
}

private suspend fun updateValues(map: Map<String, Any>) {
coroutineScope {
launch(Dispatchers.IO) {
mutex.withLock { preferences.updateValues(map) }
}
}
}

private fun SharedPreferences.fetchValues(): Map<String, Any> {
return try {
val jsonValues = preferences.getString(KEY, EMPTY_MAP) ?: EMPTY_MAP
JsonConverter.toMap(JSONObject(jsonValues))
} catch (error: Exception) {
Timber.tag(TAG).e(error, "cant fetch debug values")
preferences.edit(commit = true) { remove(KEY) }
emptyMap<String, Any>()
}
}

private fun SharedPreferences.updateValues(map: Map<String, Any>) {
try {
val jsonValues = JSONObject(map).toString()
preferences.edit(commit = true) {
putString(KEY, jsonValues)
}
} catch (error: Exception) {
Timber.tag(TAG).e(error, "cant update debug values")
}
}

private companion object {
private const val EMPTY_MAP = "{}"
private const val FILE_NAME = "debug_panel_interceptor_values"
private const val KEY = "values"
private const val TAG = "DebugPanelInterceptor"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.redmadrobot.debug.plugin.konfeature

import androidx.compose.runtime.Composable
import com.redmadrobot.debug.core.internal.CommonContainer
import com.redmadrobot.debug.core.internal.PluginDependencyContainer
import com.redmadrobot.debug.core.plugin.Plugin
import com.redmadrobot.debug.plugin.konfeaure.ui.KonfeatureScreen
import com.redmadrobot.konfeature.Konfeature

public class KonfeaturePlugin(
private val debugPanelInterceptor: KonfeatureDebugPanelInterceptor,
private val konfeature: Konfeature,
) : Plugin() {

public companion object {
private const val NAME = "KONFEATURE"
}

override fun getName(): String = NAME

override fun getPluginContainer(commonContainer: CommonContainer): PluginDependencyContainer {
return KonfeaturePluginContainer(konfeature, debugPanelInterceptor)
}

@Composable
override fun content() {
KonfeatureScreen()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.redmadrobot.debug.plugin.konfeature

import com.redmadrobot.debug.core.internal.PluginDependencyContainer
import com.redmadrobot.debug.plugin.konfeature.ui.KonfeatureViewModel
import com.redmadrobot.konfeature.Konfeature

internal class KonfeaturePluginContainer(
private val konfeature: Konfeature,
private val debugPanelInterceptor: KonfeatureDebugPanelInterceptor,
) : PluginDependencyContainer {

fun createKonfeatureViewModel(): KonfeatureViewModel {
return KonfeatureViewModel(konfeature, debugPanelInterceptor)
}
}
Loading
Loading