mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-27 14:40:22 +00:00
Add common interface over SVR implementations.
This commit is contained in:
committed by
Cody Henthorne
parent
51222738df
commit
38f2b39ac4
@@ -136,6 +136,14 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter
|
||||
}
|
||||
)
|
||||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from("SVR Playground"),
|
||||
summary = DSLSettingsText.from("Quickly test various SVR options and error conditions."),
|
||||
onClick = {
|
||||
findNavController().safeNavigate(InternalSettingsFragmentDirections.actionInternalSettingsFragmentToInternalSvrPlaygroundFragment())
|
||||
}
|
||||
)
|
||||
|
||||
switchPref(
|
||||
title = DSLSettingsText.from("'Internal Details' button"),
|
||||
summary = DSLSettingsText.from("Show a button in conversation settings that lets you see more information about a user."),
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.components.settings.app.internal.svr
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.TabRow
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.key.Key.Companion.Tab
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.fragment.app.viewModels
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import org.signal.core.ui.Rows
|
||||
import org.signal.core.ui.theme.SignalTheme
|
||||
import org.thoughtcrime.securesms.compose.ComposeFragment
|
||||
|
||||
class InternalSvrPlaygroundFragment : ComposeFragment() {
|
||||
|
||||
private val viewModel: InternalSvrPlaygroundViewModel by viewModels()
|
||||
|
||||
@Composable
|
||||
override fun FragmentContent() {
|
||||
val state: InternalSvrPlaygroundState by viewModel.state
|
||||
|
||||
SvrPlaygroundScreen(
|
||||
state = state,
|
||||
onTabSelected = viewModel::onTabSelected,
|
||||
onCreateClicked = viewModel::onCreateClicked,
|
||||
onRestoreClicked = viewModel::onRestoreClicked,
|
||||
onDeleteClicked = viewModel::onDeleteClicked,
|
||||
onPinChanged = viewModel::onPinChanged
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SvrPlaygroundScreen(
|
||||
state: InternalSvrPlaygroundState,
|
||||
modifier: Modifier = Modifier,
|
||||
onTabSelected: (SvrImplementation) -> Unit = {},
|
||||
onCreateClicked: () -> Unit = {},
|
||||
onRestoreClicked: () -> Unit = {},
|
||||
onDeleteClicked: () -> Unit = {},
|
||||
onPinChanged: (String) -> Unit = {}
|
||||
) {
|
||||
Column(modifier = modifier.fillMaxWidth()) {
|
||||
TabRow(selectedTabIndex = state.options.indexOf(state.selected)) {
|
||||
state.options.forEach { option ->
|
||||
Tab(
|
||||
text = { Text(option.title) },
|
||||
selected = option == state.selected,
|
||||
onClick = { onTabSelected(option) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Rows.TextRow(
|
||||
text = "Create backup data",
|
||||
onClick = onCreateClicked
|
||||
)
|
||||
|
||||
Rows.TextRow(
|
||||
text = "Restore backup data",
|
||||
onClick = onRestoreClicked
|
||||
)
|
||||
|
||||
Rows.TextRow(
|
||||
text = "Delete backup data",
|
||||
onClick = onDeleteClicked
|
||||
)
|
||||
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(8.dp)
|
||||
.align(Alignment.CenterVertically)
|
||||
) {
|
||||
Text(text = "PIN: ")
|
||||
}
|
||||
Column {
|
||||
TextField(
|
||||
value = state.userPin,
|
||||
onValueChange = onPinChanged,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.loading) {
|
||||
Row(modifier = Modifier.fillMaxWidth().padding(48.dp)) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight(),
|
||||
color = Color.Blue
|
||||
)
|
||||
}
|
||||
} else if (state.lastResult != null) {
|
||||
Rows.TextRow(text = state.lastResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun SvrPlaygroundScreenLightTheme() {
|
||||
SignalTheme(isDarkMode = false) {
|
||||
Surface {
|
||||
SvrPlaygroundScreen(
|
||||
state = InternalSvrPlaygroundState(
|
||||
options = persistentListOf(SvrImplementation.SVR1, SvrImplementation.SVR2)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun SvrPlaygroundScreenDarkTheme() {
|
||||
SignalTheme(isDarkMode = true) {
|
||||
Surface {
|
||||
SvrPlaygroundScreen(
|
||||
state = InternalSvrPlaygroundState(
|
||||
options = persistentListOf(SvrImplementation.SVR1, SvrImplementation.SVR2)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.thoughtcrime.securesms.components.settings.app.internal.svr
|
||||
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
data class InternalSvrPlaygroundState(
|
||||
val options: ImmutableList<SvrImplementation>,
|
||||
val selected: SvrImplementation = options[0],
|
||||
val loading: Boolean = false,
|
||||
val userPin: String = "",
|
||||
val lastResult: String? = null
|
||||
)
|
||||
|
||||
enum class SvrImplementation(
|
||||
val title: String
|
||||
) {
|
||||
SVR1("KBS"), SVR2("SVR2")
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.components.settings.app.internal.svr
|
||||
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import io.reactivex.rxjava3.kotlin.plusAssign
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import org.thoughtcrime.securesms.BuildConfig
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.whispersystems.signalservice.api.svr.SecureValueRecovery
|
||||
import org.whispersystems.signalservice.api.svr.SecureValueRecoveryV1
|
||||
|
||||
class InternalSvrPlaygroundViewModel : ViewModel() {
|
||||
|
||||
private val _state: MutableState<InternalSvrPlaygroundState> = mutableStateOf(
|
||||
InternalSvrPlaygroundState(
|
||||
options = persistentListOf(SvrImplementation.SVR1, SvrImplementation.SVR2)
|
||||
)
|
||||
)
|
||||
val state: State<InternalSvrPlaygroundState> = _state
|
||||
|
||||
private val disposables: CompositeDisposable = CompositeDisposable()
|
||||
|
||||
fun onTabSelected(svr: SvrImplementation) {
|
||||
_state.value = _state.value.copy(
|
||||
selected = svr,
|
||||
lastResult = null
|
||||
)
|
||||
}
|
||||
|
||||
fun onPinChanged(pin: String) {
|
||||
_state.value = _state.value.copy(
|
||||
userPin = pin
|
||||
)
|
||||
}
|
||||
|
||||
fun onCreateClicked() {
|
||||
_state.value = _state.value.copy(
|
||||
loading = true
|
||||
)
|
||||
|
||||
disposables += _state.value.selected.toImplementation()
|
||||
.setPin(_state.value.userPin, SignalStore.kbsValues().getOrCreateMasterKey())
|
||||
.execute()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { response ->
|
||||
_state.value = _state.value.copy(
|
||||
loading = false,
|
||||
lastResult = "${response.javaClass.simpleName}\n\n$response"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onRestoreClicked() {
|
||||
_state.value = _state.value.copy(
|
||||
loading = true
|
||||
)
|
||||
|
||||
disposables += _state.value.selected.toImplementation()
|
||||
.restoreDataPostRegistration(_state.value.userPin)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { response ->
|
||||
_state.value = _state.value.copy(
|
||||
loading = false,
|
||||
lastResult = "${response.javaClass.simpleName}\n\n$response"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun onDeleteClicked() {
|
||||
_state.value = _state.value.copy(
|
||||
loading = true
|
||||
)
|
||||
|
||||
disposables += _state.value.selected.toImplementation()
|
||||
.deleteData()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { response ->
|
||||
_state.value = _state.value.copy(
|
||||
loading = false,
|
||||
lastResult = "${response.javaClass.simpleName}\n\n$response"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
disposables.clear()
|
||||
}
|
||||
|
||||
private fun SvrImplementation.toImplementation(): SecureValueRecovery {
|
||||
return when (this) {
|
||||
SvrImplementation.SVR1 -> SecureValueRecoveryV1(ApplicationDependencies.getKeyBackupService(BuildConfig.KBS_ENCLAVE))
|
||||
SvrImplementation.SVR2 -> ApplicationDependencies.getSignalServiceAccountManager().getSecureValueRecoveryV2(BuildConfig.SVR2_MRENCLAVE)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user