Skip to content

Commit 2d9cdd1

Browse files
committed
Add Advanced Settings screen in the preferences.
1 parent 2397307 commit 2d9cdd1

File tree

17 files changed

+524
-1
lines changed

17 files changed

+524
-1
lines changed

features/preferences/impl/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ dependencies {
4242
implementation(projects.libraries.network)
4343
implementation(projects.libraries.pushstore.api)
4444
implementation(projects.libraries.pushstore.test)
45+
implementation(projects.libraries.preferences.api)
4546
implementation(projects.libraries.testtags)
4647
implementation(projects.libraries.uiStrings)
4748
implementation(projects.features.rageshake.api)
@@ -55,6 +56,7 @@ dependencies {
5556
implementation(libs.accompanist.placeholder)
5657
implementation(libs.coil.compose)
5758
implementation(libs.androidx.browser)
59+
implementation(libs.androidx.datastore.preferences)
5860
api(projects.features.preferences.api)
5961
ksp(libs.showkase.processor)
6062

features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/PreferencesFlowNode.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,12 @@ import dagger.assisted.AssistedInject
3131
import io.element.android.anvilannotations.ContributesNode
3232
import io.element.android.features.preferences.api.PreferencesEntryPoint
3333
import io.element.android.features.preferences.impl.about.AboutNode
34+
import io.element.android.features.preferences.impl.advanced.AdvancedSettingsNode
3435
import io.element.android.features.preferences.impl.analytics.AnalyticsSettingsNode
3536
import io.element.android.features.preferences.impl.developer.DeveloperSettingsNode
37+
import io.element.android.features.preferences.impl.developer.tracing.ConfigureTracingNode
3638
import io.element.android.features.preferences.impl.notifications.NotificationSettingsNode
3739
import io.element.android.features.preferences.impl.notifications.edit.EditDefaultNotificationSettingNode
38-
import io.element.android.features.preferences.impl.developer.tracing.ConfigureTracingNode
3940
import io.element.android.features.preferences.impl.root.PreferencesRootNode
4041
import io.element.android.libraries.architecture.BackstackNode
4142
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
@@ -63,6 +64,9 @@ class PreferencesFlowNode @AssistedInject constructor(
6364
@Parcelize
6465
data object DeveloperSettings : NavTarget
6566

67+
@Parcelize
68+
data object AdvancedSettings : NavTarget
69+
6670
@Parcelize
6771
data object ConfigureTracing : NavTarget
6872

@@ -106,6 +110,10 @@ class PreferencesFlowNode @AssistedInject constructor(
106110
override fun onOpenNotificationSettings() {
107111
backstack.push(NavTarget.NotificationSettings)
108112
}
113+
114+
override fun onOpenAdvancedSettings() {
115+
backstack.push(NavTarget.AdvancedSettings)
116+
}
109117
}
110118
createNode<PreferencesRootNode>(buildContext, plugins = listOf(callback))
111119
}
@@ -138,6 +146,9 @@ class PreferencesFlowNode @AssistedInject constructor(
138146
val input = EditDefaultNotificationSettingNode.Inputs(navTarget.isOneToOne)
139147
createNode<EditDefaultNotificationSettingNode>(buildContext, plugins = listOf(input))
140148
}
149+
NavTarget.AdvancedSettings -> {
150+
createNode<AdvancedSettingsNode>(buildContext)
151+
}
141152
}
142153
}
143154

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright (c) 2023 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.features.preferences.impl.advanced
18+
19+
sealed interface AdvancedSettingsEvents {
20+
data class SetRichTextEditorEnabled(val enabled: Boolean) : AdvancedSettingsEvents
21+
data class SetDeveloperModeEnabled(val enabled: Boolean) : AdvancedSettingsEvents
22+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2023 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.features.preferences.impl.advanced
18+
19+
import androidx.compose.runtime.Composable
20+
import androidx.compose.ui.Modifier
21+
import com.bumble.appyx.core.modality.BuildContext
22+
import com.bumble.appyx.core.node.Node
23+
import com.bumble.appyx.core.plugin.Plugin
24+
import dagger.assisted.Assisted
25+
import dagger.assisted.AssistedInject
26+
import io.element.android.anvilannotations.ContributesNode
27+
import io.element.android.libraries.di.SessionScope
28+
29+
@ContributesNode(SessionScope::class)
30+
class AdvancedSettingsNode @AssistedInject constructor(
31+
@Assisted buildContext: BuildContext,
32+
@Assisted plugins: List<Plugin>,
33+
private val presenter: AdvancedSettingsPresenter,
34+
) : Node(buildContext, plugins = plugins) {
35+
36+
@Composable
37+
override fun View(modifier: Modifier) {
38+
val state = presenter.present()
39+
AdvancedSettingsView(
40+
state = state,
41+
modifier = modifier,
42+
onBackPressed = ::navigateUp
43+
)
44+
}
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright (c) 2023 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.features.preferences.impl.advanced
18+
19+
import androidx.compose.runtime.Composable
20+
import androidx.compose.runtime.collectAsState
21+
import androidx.compose.runtime.getValue
22+
import androidx.compose.runtime.rememberCoroutineScope
23+
import io.element.android.features.preferences.api.store.PreferencesStore
24+
import io.element.android.libraries.architecture.Presenter
25+
import kotlinx.coroutines.launch
26+
import javax.inject.Inject
27+
28+
class AdvancedSettingsPresenter @Inject constructor(
29+
private val preferencesStore: PreferencesStore,
30+
) : Presenter<AdvancedSettingsState> {
31+
32+
@Composable
33+
override fun present(): AdvancedSettingsState {
34+
val localCoroutineScope = rememberCoroutineScope()
35+
val isRichTextEditorEnabled by preferencesStore
36+
.isRichTextEditorEnabledFlow()
37+
.collectAsState(initial = false)
38+
val isDeveloperModeEnabled by preferencesStore
39+
.isDevelopModeEnabledFlow()
40+
.collectAsState(initial = false)
41+
42+
fun handleEvents(event: AdvancedSettingsEvents) {
43+
when (event) {
44+
is AdvancedSettingsEvents.SetRichTextEditorEnabled -> localCoroutineScope.launch {
45+
preferencesStore.setRichTextEditorEnabled(event.enabled)
46+
}
47+
is AdvancedSettingsEvents.SetDeveloperModeEnabled -> localCoroutineScope.launch {
48+
preferencesStore.setDevelopModeEnabled(event.enabled)
49+
}
50+
}
51+
}
52+
53+
return AdvancedSettingsState(
54+
isRichTextEditorEnabled = isRichTextEditorEnabled,
55+
isDeveloperModeEnabled = isDeveloperModeEnabled,
56+
eventSink = ::handleEvents
57+
)
58+
}
59+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright (c) 2023 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.features.preferences.impl.advanced
18+
19+
data class AdvancedSettingsState constructor(
20+
val isRichTextEditorEnabled: Boolean,
21+
val isDeveloperModeEnabled: Boolean,
22+
val eventSink: (AdvancedSettingsEvents) -> Unit
23+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) 2023 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.features.preferences.impl.advanced
18+
19+
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
20+
21+
open class AdvancedSettingsStateProvider : PreviewParameterProvider<AdvancedSettingsState> {
22+
override val values: Sequence<AdvancedSettingsState>
23+
get() = sequenceOf(
24+
aAdvancedSettingsState(),
25+
aAdvancedSettingsState(isRichTextEditorEnabled = true),
26+
aAdvancedSettingsState(isDeveloperModeEnabled = true),
27+
)
28+
}
29+
30+
fun aAdvancedSettingsState(
31+
isRichTextEditorEnabled: Boolean = false,
32+
isDeveloperModeEnabled: Boolean = false,
33+
) = AdvancedSettingsState(
34+
isRichTextEditorEnabled = isRichTextEditorEnabled,
35+
isDeveloperModeEnabled = isDeveloperModeEnabled,
36+
eventSink = {}
37+
)
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright (c) 2023 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.features.preferences.impl.advanced
18+
19+
import androidx.compose.runtime.Composable
20+
import androidx.compose.ui.Modifier
21+
import androidx.compose.ui.res.stringResource
22+
import androidx.compose.ui.tooling.preview.PreviewParameter
23+
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
24+
import io.element.android.libraries.designsystem.components.preferences.PreferenceView
25+
import io.element.android.libraries.designsystem.preview.DayNightPreviews
26+
import io.element.android.libraries.designsystem.preview.ElementPreview
27+
import io.element.android.libraries.ui.strings.CommonStrings
28+
29+
@Composable
30+
fun AdvancedSettingsView(
31+
state: AdvancedSettingsState,
32+
onBackPressed: () -> Unit,
33+
modifier: Modifier = Modifier,
34+
) {
35+
PreferenceView(
36+
modifier = modifier,
37+
onBackPressed = onBackPressed,
38+
title = stringResource(id = CommonStrings.common_advanced_settings)
39+
) {
40+
PreferenceSwitch(
41+
title = stringResource(id = CommonStrings.common_rich_text_editor),
42+
// TODO i18n
43+
subtitle = "Disable the rich text editor to type Markdown manually",
44+
isChecked = state.isRichTextEditorEnabled,
45+
onCheckedChange = { state.eventSink(AdvancedSettingsEvents.SetRichTextEditorEnabled(it)) },
46+
)
47+
PreferenceSwitch(
48+
// TODO i18n
49+
title = "Developer mode",
50+
// TODO i18n
51+
subtitle = "The developer mode activates hidden features. For developers only!",
52+
isChecked = state.isDeveloperModeEnabled,
53+
onCheckedChange = { state.eventSink(AdvancedSettingsEvents.SetDeveloperModeEnabled(it)) },
54+
)
55+
}
56+
}
57+
58+
@DayNightPreviews
59+
@Composable
60+
internal fun AdvancedSettingsViewPreview(@PreviewParameter(AdvancedSettingsStateProvider::class) state: AdvancedSettingsState) =
61+
ElementPreview {
62+
AdvancedSettingsView(state = state, onBackPressed = { })
63+
}

features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootNode.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class PreferencesRootNode @AssistedInject constructor(
4646
fun onOpenAbout()
4747
fun onOpenDeveloperSettings()
4848
fun onOpenNotificationSettings()
49+
fun onOpenAdvancedSettings()
4950
}
5051

5152
private fun onOpenBugReport() {
@@ -60,6 +61,10 @@ class PreferencesRootNode @AssistedInject constructor(
6061
plugins<Callback>().forEach { it.onOpenDeveloperSettings() }
6162
}
6263

64+
private fun onOpenAdvancedSettings() {
65+
plugins<Callback>().forEach { it.onOpenAdvancedSettings() }
66+
}
67+
6368
private fun onOpenAnalytics() {
6469
plugins<Callback>().forEach { it.onOpenAnalytics() }
6570
}
@@ -100,6 +105,7 @@ class PreferencesRootNode @AssistedInject constructor(
100105
onOpenAbout = this::onOpenAbout,
101106
onVerifyClicked = this::onVerifyClicked,
102107
onOpenDeveloperSettings = this::onOpenDeveloperSettings,
108+
onOpenAdvancedSettings = this::onOpenAdvancedSettings,
103109
onSuccessLogout = { onSuccessLogout(activity, it) },
104110
onManageAccountClicked = { onManageAccountClicked(activity, it, isDark) },
105111
onOpenNotificationSettings = this::onOpenNotificationSettings

features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/root/PreferencesRootView.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import androidx.compose.material.icons.outlined.Help
2525
import androidx.compose.material.icons.outlined.InsertChart
2626
import androidx.compose.material.icons.outlined.Notifications
2727
import androidx.compose.material.icons.outlined.OpenInNew
28+
import androidx.compose.material.icons.outlined.Settings
2829
import androidx.compose.material.icons.outlined.VerifiedUser
2930
import androidx.compose.runtime.Composable
3031
import androidx.compose.ui.Modifier
@@ -58,6 +59,7 @@ fun PreferencesRootView(
5859
onOpenRageShake: () -> Unit,
5960
onOpenAbout: () -> Unit,
6061
onOpenDeveloperSettings: () -> Unit,
62+
onOpenAdvancedSettings: () -> Unit,
6163
onSuccessLogout: (logoutUrlResult: String?) -> Unit,
6264
onOpenNotificationSettings: () -> Unit,
6365
modifier: Modifier = Modifier,
@@ -125,6 +127,13 @@ fun PreferencesRootView(
125127
DeveloperPreferencesView(onOpenDeveloperSettings)
126128
HorizontalDivider()
127129
}
130+
HorizontalDivider()
131+
PreferenceText(
132+
title = stringResource(id = CommonStrings.common_advanced_settings),
133+
icon = Icons.Outlined.Settings,
134+
onClick = onOpenAdvancedSettings,
135+
)
136+
HorizontalDivider()
128137
LogoutPreferenceView(
129138
state = state.logoutState,
130139
onSuccessLogout = onSuccessLogout,
@@ -168,6 +177,7 @@ private fun ContentToPreview(matrixUser: MatrixUser) {
168177
onOpenAnalytics = {},
169178
onOpenRageShake = {},
170179
onOpenDeveloperSettings = {},
180+
onOpenAdvancedSettings = {},
171181
onOpenAbout = {},
172182
onVerifyClicked = {},
173183
onSuccessLogout = {},

0 commit comments

Comments
 (0)