mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 13:38:49 +03:00
adding entry points for injecting and overriding the homeserver capabilities
This commit is contained in:
parent
50740b1449
commit
3df4f1e099
11 changed files with 260 additions and 25 deletions
|
@ -22,13 +22,17 @@ import androidx.datastore.preferences.core.Preferences
|
|||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import im.vector.app.features.HomeserverCapabilitiesOverride
|
||||
import im.vector.app.features.VectorOverrides
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
|
||||
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "vector_overrides")
|
||||
private val keyForceDialPadDisplay = booleanPreferencesKey("force_dial_pad_display")
|
||||
private val keyForceLoginFallback = booleanPreferencesKey("force_login_fallback")
|
||||
private val forceCanChangeDisplayName = booleanPreferencesKey("force_can_change_display_name")
|
||||
private val forceCanChangeAvatar = booleanPreferencesKey("force_can_change_avatar")
|
||||
|
||||
class DebugVectorOverrides(private val context: Context) : VectorOverrides {
|
||||
|
||||
|
@ -40,6 +44,13 @@ class DebugVectorOverrides(private val context: Context) : VectorOverrides {
|
|||
preferences[keyForceLoginFallback].orFalse()
|
||||
}
|
||||
|
||||
override val forceHomeserverCapabilities = context.dataStore.data.map { preferences ->
|
||||
HomeserverCapabilitiesOverride(
|
||||
canChangeDisplayName = preferences[forceCanChangeDisplayName],
|
||||
canChangeAvatar = preferences[forceCanChangeAvatar]
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun setForceDialPadDisplay(force: Boolean) {
|
||||
context.dataStore.edit { settings ->
|
||||
settings[keyForceDialPadDisplay] = force
|
||||
|
@ -51,4 +62,18 @@ class DebugVectorOverrides(private val context: Context) : VectorOverrides {
|
|||
settings[keyForceLoginFallback] = force
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateHomeserverCapabilities(block: HomeserverCapabilitiesOverride.() -> HomeserverCapabilitiesOverride) {
|
||||
val capabilitiesOverride = block(forceHomeserverCapabilities.firstOrNull() ?: HomeserverCapabilitiesOverride(null, null))
|
||||
context.dataStore.edit { settings ->
|
||||
when (capabilitiesOverride.canChangeDisplayName) {
|
||||
null -> settings.remove(forceCanChangeDisplayName)
|
||||
else -> settings[forceCanChangeDisplayName] = capabilitiesOverride.canChangeDisplayName
|
||||
}
|
||||
when (capabilitiesOverride.canChangeAvatar) {
|
||||
null -> settings.remove(forceCanChangeAvatar)
|
||||
else -> settings[forceCanChangeAvatar] = capabilitiesOverride.canChangeAvatar
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,12 @@ class DebugPrivateSettingsFragment : VectorBaseFragment<FragmentDebugPrivateSett
|
|||
|
||||
override fun invalidate() = withState(viewModel) {
|
||||
views.forceDialPadTabDisplay.isChecked = it.dialPadVisible
|
||||
views.forceChangeDisplayNameCapability.bind(it.homeserverCapabilityOverrides.displayName) { option ->
|
||||
viewModel.handle(DebugPrivateSettingsViewActions.SetDisplayNameCapabilityOverride(option))
|
||||
}
|
||||
views.forceChangeAvatarCapability.bind(it.homeserverCapabilityOverrides.avatar) { option ->
|
||||
viewModel.handle(DebugPrivateSettingsViewActions.SetAvatarCapabilityOverride(option))
|
||||
}
|
||||
views.forceLoginFallback.isChecked = it.forceLoginFallback
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,9 @@ package im.vector.app.features.debug.settings
|
|||
|
||||
import im.vector.app.core.platform.VectorViewModelAction
|
||||
|
||||
sealed class DebugPrivateSettingsViewActions : VectorViewModelAction {
|
||||
data class SetDialPadVisibility(val force: Boolean) : DebugPrivateSettingsViewActions()
|
||||
data class SetForceLoginFallbackEnabled(val force: Boolean) : DebugPrivateSettingsViewActions()
|
||||
sealed interface DebugPrivateSettingsViewActions : VectorViewModelAction {
|
||||
data class SetDialPadVisibility(val force: Boolean) : DebugPrivateSettingsViewActions
|
||||
data class SetForceLoginFallbackEnabled(val force: Boolean) : DebugPrivateSettingsViewActions
|
||||
data class SetDisplayNameCapabilityOverride(val option: BooleanHomeserverCapabilitiesOverride?) : DebugPrivateSettingsViewActions
|
||||
data class SetAvatarCapabilityOverride(val option: BooleanHomeserverCapabilitiesOverride?) : DebugPrivateSettingsViewActions
|
||||
}
|
||||
|
|
|
@ -22,9 +22,12 @@ import dagger.assisted.AssistedFactory
|
|||
import dagger.assisted.AssistedInject
|
||||
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
||||
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||
import im.vector.app.core.extensions.exhaustive
|
||||
import im.vector.app.core.platform.EmptyViewEvents
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.features.debug.features.DebugVectorOverrides
|
||||
import im.vector.app.features.debug.settings.DebugPrivateSettingsViewActions.SetAvatarCapabilityOverride
|
||||
import im.vector.app.features.debug.settings.DebugPrivateSettingsViewActions.SetDisplayNameCapabilityOverride
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class DebugPrivateSettingsViewModel @AssistedInject constructor(
|
||||
|
@ -40,10 +43,10 @@ class DebugPrivateSettingsViewModel @AssistedInject constructor(
|
|||
companion object : MavericksViewModelFactory<DebugPrivateSettingsViewModel, DebugPrivateSettingsViewState> by hiltMavericksViewModelFactory()
|
||||
|
||||
init {
|
||||
observeVectorDataStore()
|
||||
observeVectorOverrides()
|
||||
}
|
||||
|
||||
private fun observeVectorDataStore() {
|
||||
private fun observeVectorOverrides() {
|
||||
debugVectorOverrides.forceDialPad.setOnEach {
|
||||
copy(
|
||||
dialPadVisible = it
|
||||
|
@ -52,13 +55,23 @@ class DebugPrivateSettingsViewModel @AssistedInject constructor(
|
|||
debugVectorOverrides.forceLoginFallback.setOnEach {
|
||||
copy(forceLoginFallback = it)
|
||||
}
|
||||
debugVectorOverrides.forceHomeserverCapabilities.setOnEach {
|
||||
val activeDisplayNameOption = BooleanHomeserverCapabilitiesOverride.from(it.canChangeDisplayName)
|
||||
val activeAvatarOption = BooleanHomeserverCapabilitiesOverride.from(it.canChangeAvatar)
|
||||
copy(homeserverCapabilityOverrides = homeserverCapabilityOverrides.copy(
|
||||
displayName = homeserverCapabilityOverrides.displayName.copy(activeOption = activeDisplayNameOption),
|
||||
avatar = homeserverCapabilityOverrides.displayName.copy(activeOption = activeAvatarOption),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
override fun handle(action: DebugPrivateSettingsViewActions) {
|
||||
when (action) {
|
||||
is DebugPrivateSettingsViewActions.SetDialPadVisibility -> handleSetDialPadVisibility(action)
|
||||
is DebugPrivateSettingsViewActions.SetForceLoginFallbackEnabled -> handleSetForceLoginFallbackEnabled(action)
|
||||
}
|
||||
is SetDisplayNameCapabilityOverride -> handSetDisplayNameCapabilityOverride(action)
|
||||
is SetAvatarCapabilityOverride -> handSetAvatarCapabilityOverride(action)
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
private fun handleSetDialPadVisibility(action: DebugPrivateSettingsViewActions.SetDialPadVisibility) {
|
||||
|
@ -72,4 +85,18 @@ class DebugPrivateSettingsViewModel @AssistedInject constructor(
|
|||
debugVectorOverrides.setForceLoginFallback(action.force)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handSetDisplayNameCapabilityOverride(action: SetDisplayNameCapabilityOverride) {
|
||||
viewModelScope.launch {
|
||||
val forceDisplayName = action.option.toBoolean()
|
||||
debugVectorOverrides.updateHomeserverCapabilities { copy(canChangeDisplayName = forceDisplayName) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun handSetAvatarCapabilityOverride(action: SetAvatarCapabilityOverride) {
|
||||
viewModelScope.launch {
|
||||
val forceAvatar = action.option.toBoolean()
|
||||
debugVectorOverrides.updateHomeserverCapabilities { copy(canChangeAvatar = forceAvatar) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,23 @@
|
|||
package im.vector.app.features.debug.settings
|
||||
|
||||
import com.airbnb.mvrx.MavericksState
|
||||
import im.vector.app.features.debug.settings.OverrideDropdownView.OverrideDropdown
|
||||
|
||||
data class DebugPrivateSettingsViewState(
|
||||
val dialPadVisible: Boolean = false,
|
||||
val forceLoginFallback: Boolean = false,
|
||||
val homeserverCapabilityOverrides: HomeserverCapabilityOverrides = HomeserverCapabilityOverrides()
|
||||
) : MavericksState
|
||||
|
||||
data class HomeserverCapabilityOverrides(
|
||||
val displayName: OverrideDropdown<BooleanHomeserverCapabilitiesOverride> = OverrideDropdown(
|
||||
label = "Override display name capability",
|
||||
activeOption = null,
|
||||
options = listOf(BooleanHomeserverCapabilitiesOverride.ForceEnabled, BooleanHomeserverCapabilitiesOverride.ForceDisabled)
|
||||
),
|
||||
val avatar: OverrideDropdown<BooleanHomeserverCapabilitiesOverride> = OverrideDropdown(
|
||||
label = "Override avatar capability",
|
||||
activeOption = null,
|
||||
options = listOf(BooleanHomeserverCapabilitiesOverride.ForceEnabled, BooleanHomeserverCapabilitiesOverride.ForceDisabled)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.settings
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.widget.AppCompatSpinner
|
||||
import im.vector.app.R
|
||||
|
||||
class OverrideDropdownView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null
|
||||
) : LinearLayout(context, attrs) {
|
||||
|
||||
private val labelView: TextView
|
||||
private val optionsSpinner: AppCompatSpinner
|
||||
|
||||
init {
|
||||
orientation = HORIZONTAL
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
inflate(context, R.layout.view_boolean_dropdown, this)
|
||||
labelView = findViewById(R.id.feature_label)
|
||||
optionsSpinner = findViewById(R.id.feature_options)
|
||||
}
|
||||
|
||||
fun <T: OverrideOption> bind(feature: OverrideDropdown<T>, listener: Listener<T>) {
|
||||
labelView.text = feature.label
|
||||
|
||||
optionsSpinner.apply {
|
||||
val arrayAdapter = ArrayAdapter<String>(context, android.R.layout.simple_spinner_dropdown_item)
|
||||
val options = listOf("Inactive") + feature.options.map { it.label }
|
||||
arrayAdapter.addAll(options)
|
||||
adapter = arrayAdapter
|
||||
|
||||
feature.activeOption?.let {
|
||||
setSelection(options.indexOf(it.label), false)
|
||||
}
|
||||
|
||||
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
when (position) {
|
||||
0 -> listener.onOverrideSelected(option = null)
|
||||
else -> listener.onOverrideSelected(feature.options[position - 1])
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun interface Listener<T> {
|
||||
fun onOverrideSelected(option: T?)
|
||||
}
|
||||
|
||||
data class OverrideDropdown<T: OverrideOption>(
|
||||
val label: String,
|
||||
val options: List<T>,
|
||||
val activeOption: T?,
|
||||
)
|
||||
}
|
||||
|
||||
interface OverrideOption {
|
||||
val label: String
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2022 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.settings
|
||||
|
||||
sealed interface BooleanHomeserverCapabilitiesOverride : OverrideOption {
|
||||
|
||||
companion object {
|
||||
fun from(value: Boolean?) = when (value) {
|
||||
null -> null
|
||||
true -> ForceEnabled
|
||||
false -> ForceDisabled
|
||||
}
|
||||
}
|
||||
|
||||
object ForceEnabled : BooleanHomeserverCapabilitiesOverride {
|
||||
override val label = "Force enabled"
|
||||
}
|
||||
|
||||
object ForceDisabled : BooleanHomeserverCapabilitiesOverride {
|
||||
override val label = "Force disabled"
|
||||
}
|
||||
}
|
||||
|
||||
fun BooleanHomeserverCapabilitiesOverride?.toBoolean() = when (this) {
|
||||
null -> null
|
||||
BooleanHomeserverCapabilitiesOverride.ForceDisabled -> false
|
||||
BooleanHomeserverCapabilitiesOverride.ForceEnabled -> true
|
||||
}
|
|
@ -31,6 +31,24 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:text="Force login and registration fallback" />
|
||||
|
||||
<im.vector.app.features.debug.settings.OverrideDropdownView
|
||||
android:id="@+id/forceChangeDisplayNameCapability"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="4dp" />
|
||||
|
||||
<im.vector.app.features.debug.settings.OverrideDropdownView
|
||||
android:id="@+id/forceChangeAvatarCapability"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="4dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
|
25
vector/src/debug/res/layout/view_boolean_dropdown.xml
Normal file
25
vector/src/debug/res/layout/view_boolean_dropdown.xml
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:parentTag="android.widget.LinearLayout">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/feature_label"
|
||||
style="@style/Widget.Vector.TextView.Subtitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:textColor="?vctr_content_primary"
|
||||
tools:text="Login version" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatSpinner
|
||||
android:id="@+id/feature_options"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
</merge>
|
|
@ -22,9 +22,16 @@ import kotlinx.coroutines.flow.flowOf
|
|||
interface VectorOverrides {
|
||||
val forceDialPad: Flow<Boolean>
|
||||
val forceLoginFallback: Flow<Boolean>
|
||||
val forceHomeserverCapabilities: Flow<HomeserverCapabilitiesOverride>?
|
||||
}
|
||||
|
||||
data class HomeserverCapabilitiesOverride(
|
||||
val canChangeDisplayName: Boolean?,
|
||||
val canChangeAvatar: Boolean?
|
||||
)
|
||||
|
||||
class DefaultVectorOverrides : VectorOverrides {
|
||||
override val forceDialPad = flowOf(false)
|
||||
override val forceLoginFallback = flowOf(false)
|
||||
override val forceHomeserverCapabilities: Flow<HomeserverCapabilitiesOverride>? = null
|
||||
}
|
||||
|
|
|
@ -90,25 +90,6 @@
|
|||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/changeProfilePictureButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:background="@drawable/bg_rounded_button"
|
||||
android:backgroundTint="?vctr_system"
|
||||
android:contentDescription="@string/ftue_profile_picture_title"
|
||||
android:padding="10dp"
|
||||
android:src="@drawable/ic_camera_plain"
|
||||
app:layout_constraintBottom_toBottomOf="@id/profilePictureView"
|
||||
app:layout_constraintEnd_toEndOf="@id/profilePictureView"
|
||||
app:layout_constraintHeight_percent="0.08"
|
||||
app:layout_constraintHorizontal_bias="1"
|
||||
app:layout_constraintStart_toStartOf="@id/profilePictureView"
|
||||
app:layout_constraintTop_toTopOf="@id/profilePictureView"
|
||||
app:layout_constraintVertical_bias="1"
|
||||
app:tint="?vctr_content_secondary" />
|
||||
|
||||
<Space
|
||||
android:id="@+id/avatarTitleSpacing"
|
||||
android:layout_width="match_parent"
|
||||
|
|
Loading…
Reference in a new issue