created hidden settings menu for ab-testing
This commit is contained in:
@ -41,6 +41,11 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".ui.DevSettingsActivity"
|
||||
android:exported="false"
|
||||
android:parentActivityName=".MainActivity" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
46
app/src/main/java/com/dano/test1/ui/AbTestSettingsStore.kt
Normal file
46
app/src/main/java/com/dano/test1/ui/AbTestSettingsStore.kt
Normal file
@ -0,0 +1,46 @@
|
||||
package com.dano.test1.ui
|
||||
|
||||
import android.content.Context
|
||||
|
||||
/**
|
||||
* Persists dev-only A/B overrides. Not a security boundary; values are for local QA.
|
||||
* Use [effectiveVariant] when branching questionnaire or remote-config logic.
|
||||
*/
|
||||
object AbTestSettingsStore {
|
||||
private const val PREF = "dev_ab_settings"
|
||||
private const val KEY_OVERRIDE = "override_enabled"
|
||||
private const val KEY_VARIANT = "variant"
|
||||
|
||||
const val VARIANT_NONE = "NONE"
|
||||
const val VARIANT_A = "A"
|
||||
const val VARIANT_B = "B"
|
||||
|
||||
fun isOverrideEnabled(context: Context): Boolean =
|
||||
context.getSharedPreferences(PREF, Context.MODE_PRIVATE).getBoolean(KEY_OVERRIDE, false)
|
||||
|
||||
fun setOverrideEnabled(context: Context, enabled: Boolean) {
|
||||
context.getSharedPreferences(PREF, Context.MODE_PRIVATE).edit()
|
||||
.putBoolean(KEY_OVERRIDE, enabled)
|
||||
.apply()
|
||||
}
|
||||
|
||||
fun getVariant(context: Context): String =
|
||||
context.getSharedPreferences(PREF, Context.MODE_PRIVATE).getString(KEY_VARIANT, VARIANT_NONE)
|
||||
?: VARIANT_NONE
|
||||
|
||||
fun setVariant(context: Context, variant: String) {
|
||||
context.getSharedPreferences(PREF, Context.MODE_PRIVATE).edit()
|
||||
.putString(KEY_VARIANT, variant)
|
||||
.apply()
|
||||
}
|
||||
|
||||
/** Returns "A" or "B" when override is on and a branch is selected; otherwise null. */
|
||||
fun effectiveVariant(context: Context): String? {
|
||||
if (!isOverrideEnabled(context)) return null
|
||||
return when (getVariant(context)) {
|
||||
VARIANT_A -> "A"
|
||||
VARIANT_B -> "B"
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
71
app/src/main/java/com/dano/test1/ui/DevSettingsActivity.kt
Normal file
71
app/src/main/java/com/dano/test1/ui/DevSettingsActivity.kt
Normal file
@ -0,0 +1,71 @@
|
||||
package com.dano.test1.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.widget.RadioButton
|
||||
import android.widget.RadioGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.SwitchCompat
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import com.dano.test1.R
|
||||
|
||||
/**
|
||||
* Dev-only A/B settings. Entry: client code [_dev_settings_] on the opening screen Load action.
|
||||
* Not a security boundary (secret string is in the APK).
|
||||
*/
|
||||
class DevSettingsActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var switchOverride: SwitchCompat
|
||||
private lateinit var radioGroup: RadioGroup
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_dev_settings)
|
||||
|
||||
val toolbar = findViewById<Toolbar>(R.id.devSettingsToolbar)
|
||||
setSupportActionBar(toolbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
switchOverride = findViewById(R.id.switchAbOverride)
|
||||
radioGroup = findViewById(R.id.radioGroupVariant)
|
||||
|
||||
applyPrefsToUi()
|
||||
|
||||
switchOverride.setOnCheckedChangeListener { _, isChecked ->
|
||||
AbTestSettingsStore.setOverrideEnabled(this, isChecked)
|
||||
updateRadiosEnabled(isChecked)
|
||||
}
|
||||
|
||||
radioGroup.setOnCheckedChangeListener { _, checkedId ->
|
||||
val variant = when (checkedId) {
|
||||
R.id.radioVariantA -> AbTestSettingsStore.VARIANT_A
|
||||
R.id.radioVariantB -> AbTestSettingsStore.VARIANT_B
|
||||
else -> AbTestSettingsStore.VARIANT_NONE
|
||||
}
|
||||
AbTestSettingsStore.setVariant(this, variant)
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyPrefsToUi() {
|
||||
val overrideOn = AbTestSettingsStore.isOverrideEnabled(this)
|
||||
switchOverride.isChecked = overrideOn
|
||||
updateRadiosEnabled(overrideOn)
|
||||
|
||||
when (AbTestSettingsStore.getVariant(this)) {
|
||||
AbTestSettingsStore.VARIANT_A -> radioGroup.check(R.id.radioVariantA)
|
||||
AbTestSettingsStore.VARIANT_B -> radioGroup.check(R.id.radioVariantB)
|
||||
else -> radioGroup.check(R.id.radioVariantDefault)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateRadiosEnabled(enabled: Boolean) {
|
||||
for (i in 0 until radioGroup.childCount) {
|
||||
(radioGroup.getChildAt(i) as? RadioButton)?.isEnabled = enabled
|
||||
}
|
||||
radioGroup.alpha = if (enabled) 1f else 0.5f
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
finish()
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
package com.dano.test1.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.widget.Button
|
||||
import android.widget.EditText
|
||||
import android.widget.Toast
|
||||
@ -24,6 +25,11 @@ class LoadButtonHandler(
|
||||
private val updateMainButtonsState: (Boolean) -> Unit,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
/** Opening-screen client code that opens dev A/B settings (not a security boundary). */
|
||||
private const val DEV_SETTINGS_SECRET = "_dev_settings_"
|
||||
}
|
||||
|
||||
fun setup() {
|
||||
loadButton.text = LanguageManager.getText(languageIDProvider(), "load")
|
||||
loadButton.setOnClickListener { handleLoadButton() }
|
||||
@ -37,6 +43,12 @@ class LoadButtonHandler(
|
||||
return
|
||||
}
|
||||
|
||||
if (inputText == DEV_SETTINGS_SECRET) {
|
||||
editText.text.clear()
|
||||
activity.startActivity(Intent(activity, DevSettingsActivity::class.java))
|
||||
return
|
||||
}
|
||||
|
||||
buttonPoints.clear()
|
||||
setButtonsEnabled(emptyList()) // temporär sperren
|
||||
updateButtonTexts() // Chips zeigen vorläufig „Gesperrt“
|
||||
|
||||
75
app/src/main/res/layout/activity_dev_settings.xml
Normal file
75
app/src/main/res/layout/activity_dev_settings.xml
Normal file
@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/devSettingsToolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:title="A/B testing (dev)"
|
||||
app:titleTextColor="@android:color/white" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="8dp"
|
||||
android:text="Manual A/B override for local testing. Not a security boundary."
|
||||
android:textSize="14sp" />
|
||||
|
||||
<androidx.appcompat.widget.SwitchCompat
|
||||
android:id="@+id/switchAbOverride"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="8dp"
|
||||
android:text="Manual A/B override" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:text="Variant"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/radioGroupVariant"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/radioVariantDefault"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Default (no override)" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/radioVariantA"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Variant A" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/radioVariantB"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Variant B" />
|
||||
</RadioGroup>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
||||
Reference in New Issue
Block a user