diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/BooleanFeatureItem.kt b/vector/src/debug/java/im/vector/app/features/debug/features/BooleanFeatureItem.kt new file mode 100644 index 0000000000..53714cbd59 --- /dev/null +++ b/vector/src/debug/java/im/vector/app/features/debug/features/BooleanFeatureItem.kt @@ -0,0 +1,86 @@ +/* + * Copyright 2019 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package im.vector.app.features.debug.features + +import android.view.View +import android.widget.AdapterView +import android.widget.ArrayAdapter +import android.widget.Spinner +import android.widget.TextView +import com.airbnb.epoxy.EpoxyAttribute +import com.airbnb.epoxy.EpoxyModelClass +import im.vector.app.core.epoxy.VectorEpoxyHolder +import im.vector.app.core.epoxy.VectorEpoxyModel + +@EpoxyModelClass(layout = im.vector.app.R.layout.item_feature) +abstract class BooleanFeatureItem : VectorEpoxyModel() { + + @EpoxyAttribute + lateinit var feature: Feature.BooleanFeature + + @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) + var listener: Listener? = null + + override fun bind(holder: Holder) { + super.bind(holder) + holder.label.text = feature.label + + holder.optionsSpinner.apply { + val arrayAdapter = ArrayAdapter(context, android.R.layout.simple_spinner_dropdown_item) + val options = listOf( + "DEFAULT - ${feature.default.toEmoji()}", + "✅", + "❌" + ) + arrayAdapter.addAll(options) + adapter = arrayAdapter + + feature.override?.let { + setSelection(options.indexOf(it.toEmoji()), false) + } + + onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + when (position) { + 0 -> listener?.onBooleanOptionSelected(option = null, feature) + else -> listener?.onBooleanOptionSelected(options[position].fromEmoji(), feature) + } + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + // do nothing + } + } + } + } + + class Holder : VectorEpoxyHolder() { + val label by bind(im.vector.app.R.id.feature_label) + val optionsSpinner by bind(im.vector.app.R.id.feature_options) + } + + interface Listener { + fun onBooleanOptionSelected(option: Boolean?, feature: Feature.BooleanFeature) + } +} + +private fun Boolean.toEmoji() = if (this) "✅" else "❌" +private fun String.fromEmoji() = when (this) { + "✅" -> true + "❌" -> false + else -> error("unexpected input $this") +} diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesSettingsActivity.kt b/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesSettingsActivity.kt index e31f073614..dcd42bedcf 100644 --- a/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesSettingsActivity.kt +++ b/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesSettingsActivity.kt @@ -35,10 +35,14 @@ class DebugFeaturesSettingsActivity : VectorBaseActivity> onOptionSelected(option: T?, feature: Feature.EnumFeature) { + controller.listener = object : FeaturesController.Listener { + override fun > onEnumOptionSelected(option: T?, feature: Feature.EnumFeature) { debugFeatures.overrideEnum(option, feature.type) } + + override fun onBooleanOptionSelected(option: Boolean?, feature: Feature.BooleanFeature) { + debugFeatures.override(option, feature.key) + } } views.genericRecyclerView.configureWith(controller) controller.setData(debugFeaturesStateFactory.create()) diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt b/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt index 637071dd59..09913697be 100644 --- a/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt +++ b/vector/src/debug/java/im/vector/app/features/debug/features/DebugFeaturesStateFactory.kt @@ -28,16 +28,25 @@ class DebugFeaturesStateFactory @Inject constructor( return FeaturesState(listOf( createEnumFeature( label = "Onboarding variant", - selection = debugFeatures.onboardingVariant(), + override = debugFeatures.onboardingVariant(), default = defaultFeatures.onboardingVariant() + ), + + Feature.BooleanFeature( + label = "Splash - already has an account", + override = debugFeatures.isAlreadyHaveAccountSplashEnabled().takeIf { + debugFeatures.hasOverride(DebugFeatureKeys.alreadyHaveAnAccount) + }, + default = defaultFeatures.isAlreadyHaveAccountSplashEnabled(), + key = DebugFeatureKeys.alreadyHaveAnAccount ) )) } - private inline fun > createEnumFeature(label: String, selection: T, default: T): Feature { + private inline fun > createEnumFeature(label: String, override: T, default: T): Feature { return Feature.EnumFeature( label = label, - selection = selection.takeIf { debugFeatures.hasEnumOverride(T::class) }, + override = override.takeIf { debugFeatures.hasEnumOverride(T::class) }, default = default, options = enumValues().toList(), type = T::class diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt b/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt index 4dbb6a5698..daab981956 100644 --- a/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt +++ b/vector/src/debug/java/im/vector/app/features/debug/features/DebugVectorFeatures.kt @@ -20,6 +20,7 @@ import android.content.Context import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.MutablePreferences import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore @@ -42,13 +43,26 @@ class DebugVectorFeatures( return readPreferences().getEnum() ?: vectorFeatures.onboardingVariant() } + override fun isAlreadyHaveAccountSplashEnabled(): Boolean = readPreferences()[DebugFeatureKeys.alreadyHaveAnAccount] + ?: vectorFeatures.isAlreadyHaveAccountSplashEnabled() + + fun override(value: T?, key: Preferences.Key) = updatePreferences { + if (value == null) { + it.remove(key) + } else { + it[key] = value + } + } + + fun hasOverride(key: Preferences.Key) = readPreferences().contains(key) + fun > hasEnumOverride(type: KClass) = readPreferences().containsEnum(type) - fun > overrideEnum(value: T?, type: KClass) { + fun > overrideEnum(value: T?, type: KClass) = updatePreferences { if (value == null) { - updatePreferences { it.removeEnum(type) } + it.removeEnum(type) } else { - updatePreferences { it.putEnum(value, type) } + it.putEnum(value, type) } } @@ -76,3 +90,8 @@ private inline fun > Preferences.getEnum(): T? { private inline fun > enumPreferencesKey() = enumPreferencesKey(T::class) private fun > enumPreferencesKey(type: KClass) = stringPreferencesKey("enum-${type.simpleName}") + +object DebugFeatureKeys { + + val alreadyHaveAnAccount = booleanPreferencesKey("already-have-an-account") +} diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/EnumFeatureItem.kt b/vector/src/debug/java/im/vector/app/features/debug/features/EnumFeatureItem.kt index 5dd2f9efa9..d5b2ec1080 100644 --- a/vector/src/debug/java/im/vector/app/features/debug/features/EnumFeatureItem.kt +++ b/vector/src/debug/java/im/vector/app/features/debug/features/EnumFeatureItem.kt @@ -45,14 +45,14 @@ abstract class EnumFeatureItem : VectorEpoxyModel() { arrayAdapter.addAll(feature.options.map { it.name }) adapter = arrayAdapter - feature.selection?.let { + feature.override?.let { setSelection(feature.options.indexOf(it) + 1, false) } onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { when (position) { - 0 -> listener?.onOptionSelected(option = null, feature) + 0 -> listener?.onEnumOptionSelected(option = null, feature) else -> feature.onOptionSelected(position - 1) } } @@ -65,7 +65,7 @@ abstract class EnumFeatureItem : VectorEpoxyModel() { } private fun > Feature.EnumFeature.onOptionSelected(selection: Int) { - listener?.onOptionSelected(options[selection], this) + listener?.onEnumOptionSelected(options[selection], this) } class Holder : VectorEpoxyHolder() { @@ -74,6 +74,6 @@ abstract class EnumFeatureItem : VectorEpoxyModel() { } interface Listener { - fun > onOptionSelected(option: T?, feature: Feature.EnumFeature) + fun > onEnumOptionSelected(option: T?, feature: Feature.EnumFeature) } } diff --git a/vector/src/debug/java/im/vector/app/features/debug/features/FeaturesController.kt b/vector/src/debug/java/im/vector/app/features/debug/features/FeaturesController.kt index 0a05c76d69..cc4cf388d1 100644 --- a/vector/src/debug/java/im/vector/app/features/debug/features/FeaturesController.kt +++ b/vector/src/debug/java/im/vector/app/features/debug/features/FeaturesController.kt @@ -16,6 +16,7 @@ package im.vector.app.features.debug.features +import androidx.datastore.preferences.core.Preferences import com.airbnb.epoxy.TypedEpoxyController import javax.inject.Inject import kotlin.reflect.KClass @@ -28,16 +29,23 @@ sealed interface Feature { data class EnumFeature>( val label: String, - val selection: T?, + val override: T?, val default: T, val options: List, val type: KClass ) : Feature + + data class BooleanFeature( + val label: String, + val override: Boolean?, + val default: Boolean, + val key: Preferences.Key + ) : Feature } class FeaturesController @Inject constructor() : TypedEpoxyController() { - var listener: EnumFeatureItem.Listener? = null + var listener: Listener? = null override fun buildModels(data: FeaturesState?) { if (data == null) return @@ -49,7 +57,14 @@ class FeaturesController @Inject constructor() : TypedEpoxyController booleanFeatureItem { + id(index) + feature(feature) + listener(this@FeaturesController.listener) + } } } } + + interface Listener : EnumFeatureItem.Listener, BooleanFeatureItem.Listener } diff --git a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt index 4228c6ebcd..0349b15c4d 100644 --- a/vector/src/main/java/im/vector/app/features/VectorFeatures.kt +++ b/vector/src/main/java/im/vector/app/features/VectorFeatures.kt @@ -22,6 +22,8 @@ interface VectorFeatures { fun onboardingVariant(): OnboardingVariant + fun isAlreadyHaveAccountSplashEnabled(): Boolean + enum class OnboardingVariant { LEGACY, LOGIN_2, @@ -36,4 +38,5 @@ interface VectorFeatures { class DefaultVectorFeatures : VectorFeatures { override fun onboardingVariant(): VectorFeatures.OnboardingVariant = BuildConfig.ONBOARDING_VARIANT + override fun isAlreadyHaveAccountSplashEnabled() = true }