Use EventSink lambda in state instead of Flow in Presenter

This commit is contained in:
ganfra 2023-01-11 15:53:52 +01:00
parent 56e54bb172
commit 1a0c9df1da
43 changed files with 277 additions and 490 deletions

View file

@ -33,43 +33,12 @@ class BugReportNode @AssistedInject constructor(
BugReportView(
state = state,
modifier = modifier,
onDescriptionChanged = this::onDescriptionChanged,
onSetSendLog = this::onSetSendLog,
onSetSendCrashLog = this::onSetSendCrashLog,
onSetCanContact = this::onSetCanContact,
onSetSendScreenshot = this::onSetSendScreenshot,
onSubmit = this::onSubmit,
onDone = this::onDone
)
}
private fun onDone() {
presenterConnector.emitEvent(BugReportEvents.ResetAll)
plugins<Callback>().forEach { it.onBugReportSent() }
}
private fun onSubmit() {
presenterConnector.emitEvent(BugReportEvents.SendBugReport)
}
private fun onSetSendLog(sendLog: Boolean) {
presenterConnector.emitEvent(BugReportEvents.SetSendLog(sendLog))
}
private fun onSetSendCrashLog(sendCrashLog: Boolean) {
presenterConnector.emitEvent(BugReportEvents.SetSendCrashLog(sendCrashLog))
}
private fun onSetSendScreenshot(sendScreenshot: Boolean) {
presenterConnector.emitEvent(BugReportEvents.SetSendScreenshot(sendScreenshot))
}
private fun onSetCanContact(canContact: Boolean) {
presenterConnector.emitEvent(BugReportEvents.SetCanContact(canContact))
}
private fun onDescriptionChanged(description: String) {
presenterConnector.emitEvent(BugReportEvents.SetDescription(description))
}
}

View file

@ -1,10 +1,8 @@
package io.element.android.x.features.rageshake.bugreport
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -18,7 +16,6 @@ import io.element.android.x.features.rageshake.reporter.BugReporter
import io.element.android.x.features.rageshake.reporter.ReportType
import io.element.android.x.features.rageshake.screenshot.ScreenshotHolder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -27,7 +24,7 @@ class BugReportPresenter @Inject constructor(
private val crashDataStore: CrashDataStore,
private val screenshotHolder: ScreenshotHolder,
private val appCoroutineScope: CoroutineScope,
) : Presenter<BugReportState, BugReportEvents> {
) : Presenter<BugReportState> {
private class BugReporterUploadListener(
private val sendingProgress: MutableState<Float>,
@ -56,7 +53,7 @@ class BugReportPresenter @Inject constructor(
}
@Composable
override fun present(events: Flow<BugReportEvents>): BugReportState {
override fun present(): BugReportState {
val screenshotUri = rememberSaveable {
mutableStateOf(
screenshotHolder.getFile()?.toUri()?.toString()
@ -76,58 +73,54 @@ class BugReportPresenter @Inject constructor(
mutableStateOf(BugReportFormState.Default)
}
val uploadListener = BugReporterUploadListener(sendingProgress, sendingAction)
val state by remember {
derivedStateOf {
BugReportState(
hasCrashLogs = crashInfo.isNotEmpty(),
sendingProgress = sendingProgress.value,
sending = sendingAction.value,
formState = formState.value,
screenshotUri = screenshotUri.value
)
}
}
LaunchedEffect(Unit) {
events.collect { event ->
when (event) {
BugReportEvents.SendBugReport -> appCoroutineScope.sendBugReport(state, uploadListener)
BugReportEvents.ResetAll -> appCoroutineScope.resetAll()
is BugReportEvents.SetDescription -> updateFormState(formState) {
copy(description = event.description)
}
is BugReportEvents.SetCanContact -> updateFormState(formState) {
copy(canContact = event.canContact)
}
is BugReportEvents.SetSendCrashLog -> updateFormState(formState) {
copy(sendCrashLogs = event.sendCrashlog)
}
is BugReportEvents.SetSendLog -> updateFormState(formState) {
copy(sendLogs = event.sendLog)
}
is BugReportEvents.SetSendScreenshot -> updateFormState(formState) {
copy(sendScreenshot = event.sendScreenshot)
}
fun handleEvents(event: BugReportEvents) {
when (event) {
BugReportEvents.SendBugReport -> appCoroutineScope.sendBugReport(formState.value, crashInfo.isNotEmpty(), uploadListener)
BugReportEvents.ResetAll -> appCoroutineScope.resetAll()
is BugReportEvents.SetDescription -> updateFormState(formState) {
copy(description = event.description)
}
is BugReportEvents.SetCanContact -> updateFormState(formState) {
copy(canContact = event.canContact)
}
is BugReportEvents.SetSendCrashLog -> updateFormState(formState) {
copy(sendCrashLogs = event.sendCrashlog)
}
is BugReportEvents.SetSendLog -> updateFormState(formState) {
copy(sendLogs = event.sendLog)
}
is BugReportEvents.SetSendScreenshot -> updateFormState(formState) {
copy(sendScreenshot = event.sendScreenshot)
}
}
}
return state
return BugReportState(
hasCrashLogs = crashInfo.isNotEmpty(),
sendingProgress = sendingProgress.value,
sending = sendingAction.value,
formState = formState.value,
screenshotUri = screenshotUri.value,
eventSink = ::handleEvents
)
}
private fun updateFormState(formState: MutableState<BugReportFormState>, operation: BugReportFormState.() -> BugReportFormState) {
formState.value = operation(formState.value)
}
private fun CoroutineScope.sendBugReport(state: BugReportState, listener: BugReporter.IMXBugReportListener) = launch {
private fun CoroutineScope.sendBugReport(formState: BugReportFormState, hasCrashLogs: Boolean, listener: BugReporter.IMXBugReportListener) = launch {
bugReporter.sendBugReport(
coroutineScope = this,
reportType = ReportType.BUG_REPORT,
withDevicesLogs = state.formState.sendLogs,
withCrashLogs = state.hasCrashLogs && state.formState.sendCrashLogs,
withDevicesLogs = formState.sendLogs,
withCrashLogs = hasCrashLogs && formState.sendCrashLogs,
withKeyRequestHistory = false,
withScreenshot = state.formState.sendScreenshot,
theBugDescription = state.formState.description,
withScreenshot = formState.sendScreenshot,
theBugDescription = formState.description,
serverVersion = "",
canContact = state.formState.canContact,
canContact = formState.canContact,
customFields = emptyMap(),
listener = listener
)

View file

@ -26,6 +26,7 @@ data class BugReportState(
val screenshotUri: String? = null,
val sendingProgress: Float = 0F,
val sending: Async<Unit> = Async.Uninitialized,
val eventSink: (BugReportEvents) -> Unit = {}
) {
val submitEnabled =
formState.description.length > 10 && sending !is Async.Loading

View file

@ -14,7 +14,6 @@
* limitations under the License.
*/
@file:OptIn(ExperimentalMaterial3Api::class)
package io.element.android.x.features.rageshake.bugreport
@ -36,6 +35,7 @@ import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
@ -59,22 +59,21 @@ import io.element.android.x.designsystem.components.LabelledCheckbox
import io.element.android.x.designsystem.components.dialogs.ErrorDialog
import io.element.android.x.element.resources.R as ElementR
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BugReportView(
state: BugReportState,
modifier: Modifier = Modifier,
onDescriptionChanged: (String) -> Unit = {},
onSetSendLog: (Boolean) -> Unit = {},
onSetSendCrashLog: (Boolean) -> Unit = {},
onSetCanContact: (Boolean) -> Unit = {},
onSetSendScreenshot: (Boolean) -> Unit = {},
onSubmit: () -> Unit = {},
onFailureDialogClosed: () -> Unit = { },
onDone: () -> Unit = { },
) {
LogCompositions(tag = "Rageshake", msg = "Root")
val eventSink = state.eventSink
if (state.sending is Async.Success) {
onDone()
LaunchedEffect(state.sending) {
eventSink(BugReportEvents.ResetAll)
onDone()
}
return
}
Surface(
modifier = modifier,
@ -132,7 +131,7 @@ fun BugReportView(
},
onValueChange = {
descriptionFieldState = it
onDescriptionChanged(it)
eventSink(BugReportEvents.SetDescription(it))
},
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Text,
@ -143,28 +142,28 @@ fun BugReportView(
}
LabelledCheckbox(
checked = state.formState.sendLogs,
onCheckedChange = onSetSendLog,
onCheckedChange = { eventSink(BugReportEvents.SetSendLog(it)) },
enabled = isFormEnabled,
text = stringResource(id = ElementR.string.send_bug_report_include_logs)
)
if (state.hasCrashLogs) {
LabelledCheckbox(
checked = state.formState.sendCrashLogs,
onCheckedChange = onSetSendCrashLog,
onCheckedChange = { eventSink(BugReportEvents.SetSendCrashLog(it)) },
enabled = isFormEnabled,
text = stringResource(id = ElementR.string.send_bug_report_include_crash_logs)
)
}
LabelledCheckbox(
checked = state.formState.canContact,
onCheckedChange = onSetCanContact,
onCheckedChange = { eventSink(BugReportEvents.SetCanContact(it)) },
enabled = isFormEnabled,
text = stringResource(id = ElementR.string.you_may_contact_me)
)
if (state.screenshotUri != null) {
LabelledCheckbox(
checked = state.formState.sendScreenshot,
onCheckedChange = onSetSendScreenshot,
onCheckedChange = { eventSink(BugReportEvents.SetSendScreenshot(it)) },
enabled = isFormEnabled,
text = stringResource(id = ElementR.string.send_bug_report_include_screenshot)
)
@ -187,7 +186,7 @@ fun BugReportView(
}
// Submit
Button(
onClick = onSubmit,
onClick = { eventSink(BugReportEvents.SendBugReport) },
enabled = state.submitEnabled,
modifier = Modifier
.fillMaxWidth()
@ -197,7 +196,6 @@ fun BugReportView(
}
}
when (state.sending) {
Async.Uninitialized -> Unit
is Async.Loading -> {
CircularProgressIndicator(
progress = state.sendingProgress,
@ -206,9 +204,8 @@ fun BugReportView(
}
is Async.Failure -> ErrorDialog(
content = state.sending.error.toString(),
onDismiss = onFailureDialogClosed,
)
is Async.Success -> onDone()
else -> Unit
}
}
}

View file

@ -1,30 +1,31 @@
package io.element.android.x.features.rageshake.crash.ui
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.rememberCoroutineScope
import io.element.android.x.architecture.Presenter
import io.element.android.x.features.rageshake.crash.CrashDataStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import javax.inject.Inject
class CrashDetectionPresenter @Inject constructor(private val crashDataStore: CrashDataStore) : Presenter<CrashDetectionState, CrashDetectionEvents> {
class CrashDetectionPresenter @Inject constructor(private val crashDataStore: CrashDataStore) : Presenter<CrashDetectionState> {
@Composable
override fun present(events: Flow<CrashDetectionEvents>): CrashDetectionState {
override fun present(): CrashDetectionState {
val localCoroutineScope = rememberCoroutineScope()
val crashDetected = crashDataStore.appHasCrashed().collectAsState(initial = false)
LaunchedEffect(Unit) {
events.collect { event ->
when (event) {
CrashDetectionEvents.ResetAllCrashData -> resetAll()
CrashDetectionEvents.ResetAppHasCrashed -> resetAppHasCrashed()
}
fun handleEvents(event: CrashDetectionEvents) {
when (event) {
CrashDetectionEvents.ResetAllCrashData -> localCoroutineScope.resetAll()
CrashDetectionEvents.ResetAppHasCrashed -> localCoroutineScope.resetAppHasCrashed()
}
}
return CrashDetectionState(
crashDetected = crashDetected.value
crashDetected = crashDetected.value,
eventSink = ::handleEvents
)
}

View file

@ -28,15 +28,19 @@ import io.element.android.x.element.resources.R as ElementR
fun CrashDetectionView(
state: CrashDetectionState,
onOpenBugReport: () -> Unit = { },
onPopupDismissed: () -> Unit = {}
) {
LogCompositions(tag = "Crash", msg = "CrashDetectionScreen")
fun onPopupDismissed(){
state.eventSink(CrashDetectionEvents.ResetAllCrashData)
}
if (state.crashDetected) {
CrashDetectionContent(
state,
onYesClicked = onOpenBugReport,
onNoClicked = onPopupDismissed,
onDismiss = onPopupDismissed,
onNoClicked = ::onPopupDismissed,
onDismiss = ::onPopupDismissed,
)
}
}

View file

@ -18,4 +18,5 @@ package io.element.android.x.features.rageshake.crash.ui
data class CrashDetectionState(
val crashDetected: Boolean = false,
val eventSink: (CrashDetectionEvents) -> Unit = {}
)

View file

@ -3,19 +3,17 @@ package io.element.android.x.features.rageshake.detection
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import io.element.android.x.architecture.Presenter
import io.element.android.x.architecture.SharedFlowHolder
import io.element.android.x.core.screenshot.ImageResult
import io.element.android.x.features.rageshake.preferences.RageshakePreferencesEvents
import io.element.android.x.features.rageshake.preferences.RageshakePreferencesPresenter
import io.element.android.x.features.rageshake.rageshake.RageShake
import io.element.android.x.features.rageshake.screenshot.ScreenshotHolder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@ -24,13 +22,12 @@ class RageshakeDetectionPresenter @Inject constructor(
private val screenshotHolder: ScreenshotHolder,
private val rageShake: RageShake,
private val preferencesPresenter: RageshakePreferencesPresenter,
) : Presenter<RageshakeDetectionState, RageshakeDetectionEvents> {
private val preferencesEventsFlow = SharedFlowHolder<RageshakePreferencesEvents>()
) : Presenter<RageshakeDetectionState> {
@Composable
override fun present(events: Flow<RageshakeDetectionEvents>): RageshakeDetectionState {
val preferencesState = preferencesPresenter.present(events = preferencesEventsFlow.asSharedFlow())
override fun present(): RageshakeDetectionState {
val localCoroutineScope = rememberCoroutineScope()
val preferencesState = preferencesPresenter.present()
val isStarted = rememberSaveable {
mutableStateOf(false)
}
@ -40,33 +37,38 @@ class RageshakeDetectionPresenter @Inject constructor(
val showDialog = rememberSaveable {
mutableStateOf(false)
}
fun handleEvents(event: RageshakeDetectionEvents) {
when (event) {
RageshakeDetectionEvents.Disable -> {
preferencesState.eventSink(RageshakePreferencesEvents.SetIsEnabled(false))
showDialog.value = false
}
RageshakeDetectionEvents.StartDetection -> isStarted.value = true
RageshakeDetectionEvents.StopDetection -> isStarted.value = false
is RageshakeDetectionEvents.ProcessScreenshot -> localCoroutineScope.processScreenshot(takeScreenshot, showDialog, event.imageResult)
RageshakeDetectionEvents.Dismiss -> showDialog.value = false
}
}
val state = remember(preferencesState, isStarted.value, takeScreenshot.value, showDialog.value) {
RageshakeDetectionState(
isStarted = isStarted.value,
takeScreenshot = takeScreenshot.value,
showDialog = showDialog.value,
preferenceState = preferencesState
preferenceState = preferencesState,
eventSink = ::handleEvents
)
}
LaunchedEffect(Unit) {
events.collect { event ->
when (event) {
RageshakeDetectionEvents.Disable -> preferencesEventsFlow.emit(RageshakePreferencesEvents.SetIsEnabled(false))
RageshakeDetectionEvents.StartDetection -> isStarted.value = true
RageshakeDetectionEvents.StopDetection -> isStarted.value = false
is RageshakeDetectionEvents.ProcessScreenshot -> processScreenshot(takeScreenshot, showDialog, event.imageResult)
RageshakeDetectionEvents.Dismiss -> showDialog.value = false
}
}
}
LaunchedEffect(preferencesState.sensitivity) {
rageShake.setSensitivity(preferencesState.sensitivity)
}
val shouldStart = preferencesState.isEnabled &&
preferencesState.isSupported &&
isStarted.value &&
!takeScreenshot.value &&
!showDialog.value
preferencesState.isSupported &&
isStarted.value &&
!takeScreenshot.value &&
!showDialog.value
LaunchedEffect(shouldStart) {
handleRageShake(shouldStart, state, takeScreenshot)

View file

@ -24,5 +24,6 @@ data class RageshakeDetectionState(
val takeScreenshot: Boolean = false,
val showDialog: Boolean = false,
val isStarted: Boolean = false,
val preferenceState: RageshakePreferencesState = RageshakePreferencesState()
val preferenceState: RageshakePreferencesState = RageshakePreferencesState(),
val eventSink: (RageshakeDetectionEvents) -> Unit = {}
)

View file

@ -22,7 +22,9 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.Lifecycle
import io.element.android.x.core.compose.LogCompositions
import io.element.android.x.core.compose.OnLifecycleEvent
import io.element.android.x.core.hardware.vibrate
import io.element.android.x.core.screenshot.ImageResult
import io.element.android.x.core.screenshot.screenshot
@ -34,24 +36,28 @@ import io.element.android.x.element.resources.R as ElementR
fun RageshakeDetectionView(
state: RageshakeDetectionState,
onOpenBugReport: () -> Unit = { },
onScreenshotTaken: (ImageResult) -> Unit = {},
onDisableClicked: () -> Unit = {},
onNoClicked: () -> Unit = {}
) {
LogCompositions(tag = "Rageshake", msg = "RageshakeDetectionScreen")
val eventSink = state.eventSink
val context = LocalContext.current
OnLifecycleEvent { _, event ->
when (event) {
Lifecycle.Event.ON_RESUME -> eventSink(RageshakeDetectionEvents.StartDetection)
Lifecycle.Event.ON_PAUSE -> eventSink(RageshakeDetectionEvents.StopDetection)
else -> Unit
}
}
when {
state.takeScreenshot -> TakeScreenshot(
onScreenshotTaken = onScreenshotTaken
onScreenshotTaken = { eventSink(RageshakeDetectionEvents.ProcessScreenshot(it)) }
)
state.showDialog -> {
LaunchedEffect(key1 = "RS_diag") {
LaunchedEffect(Unit) {
context.vibrate()
}
RageshakeDialogContent(
state,
onNoClicked = onNoClicked,
onDisableClicked = onDisableClicked,
onNoClicked = { eventSink(RageshakeDetectionEvents.Dismiss) },
onDisableClicked = { eventSink(RageshakeDetectionEvents.Disable) },
onYesClicked = onOpenBugReport
)
}
@ -72,7 +78,6 @@ private fun TakeScreenshot(
@Composable
fun RageshakeDialogContent(
state: RageshakeDetectionState,
onNoClicked: () -> Unit = { },
onDisableClicked: () -> Unit = { },
onYesClicked: () -> Unit = { },
@ -83,6 +88,7 @@ fun RageshakeDialogContent(
thirdButtonText = stringResource(id = ElementR.string.action_disable),
submitText = stringResource(id = ElementR.string.yes),
cancelText = stringResource(id = ElementR.string.no),
onCancelClicked = onNoClicked,
onThirdButtonClicked = onDisableClicked,
onSubmitClicked = onYesClicked,
onDismiss = onNoClicked,
@ -93,8 +99,6 @@ fun RageshakeDialogContent(
@Composable
fun RageshakeDialogContentPreview() {
ElementXTheme {
RageshakeDialogContent(
state = RageshakeDetectionState()
)
RageshakeDialogContent()
}
}

View file

@ -1,26 +1,26 @@
package io.element.android.x.features.rageshake.preferences
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import io.element.android.x.architecture.Presenter
import io.element.android.x.features.rageshake.rageshake.RageShake
import io.element.android.x.features.rageshake.rageshake.RageshakeDataStore
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch
import javax.inject.Inject
class RageshakePreferencesPresenter @Inject constructor(
private val rageshake: RageShake,
private val rageshakeDataStore: RageshakeDataStore,
) : Presenter<RageshakePreferencesState, RageshakePreferencesEvents> {
) : Presenter<RageshakePreferencesState> {
@Composable
override fun present(events: Flow<RageshakePreferencesEvents>): RageshakePreferencesState {
override fun present(): RageshakePreferencesState {
val localCoroutineScope = rememberCoroutineScope()
val isSupported: MutableState<Boolean> = rememberSaveable {
mutableStateOf(rageshake.isAvailable())
}
@ -32,19 +32,18 @@ class RageshakePreferencesPresenter @Inject constructor(
.sensitivity()
.collectAsState(initial = 0f)
LaunchedEffect(Unit) {
events.collect { event ->
when (event) {
is RageshakePreferencesEvents.SetIsEnabled -> setIsEnabled(event.isEnabled)
is RageshakePreferencesEvents.SetSensitivity -> setSensitivity(event.sensitivity)
}
fun handleEvents(event: RageshakePreferencesEvents) {
when (event) {
is RageshakePreferencesEvents.SetIsEnabled -> localCoroutineScope.setIsEnabled(event.isEnabled)
is RageshakePreferencesEvents.SetSensitivity -> localCoroutineScope.setSensitivity(event.sensitivity)
}
}
return RageshakePreferencesState(
isEnabled = isEnabled.value,
isSupported = isSupported.value,
sensitivity = sensitivity.value
sensitivity = sensitivity.value,
eventSink = ::handleEvents
)
}

View file

@ -4,4 +4,5 @@ data class RageshakePreferencesState(
val isEnabled: Boolean = false,
val isSupported: Boolean = true,
val sensitivity: Float = 0.3f,
val eventSink: (RageshakePreferencesEvents) -> Unit = {},
)

View file

@ -34,9 +34,16 @@ fun RageshakePreferencesView(
state: RageshakePreferencesState,
modifier: Modifier = Modifier,
onOpenRageshake: () -> Unit = {},
onIsEnabledChanged: (Boolean) -> Unit = {},
onSensitivityChanged: (Float) -> Unit = {}
) {
fun onSensitivityChanged(sensitivity: Float){
state.eventSink(RageshakePreferencesEvents.SetSensitivity(sensitivity = sensitivity))
}
fun onEnabledChanged(isEnabled: Boolean){
state.eventSink(RageshakePreferencesEvents.SetIsEnabled(isEnabled = isEnabled))
}
Column(modifier = modifier) {
PreferenceCategory(title = stringResource(id = ElementR.string.send_bug_report)) {
PreferenceText(
@ -45,12 +52,13 @@ fun RageshakePreferencesView(
onClick = onOpenRageshake
)
}
PreferenceCategory(title = stringResource(id = ElementR.string.settings_rageshake)) {
if (state.isSupported) {
PreferenceSwitch(
title = stringResource(id = ElementR.string.send_bug_report_rage_shake),
isChecked = state.isEnabled,
onCheckedChange = onIsEnabledChanged
onCheckedChange = ::onEnabledChanged
)
PreferenceSlide(
title = stringResource(id = ElementR.string.settings_rageshake_detection_threshold),
@ -58,7 +66,7 @@ fun RageshakePreferencesView(
value = state.sensitivity,
enabled = state.isEnabled,
steps = 3 /* 5 possible values - steps are in ]0, 1[ */,
onValueChange = onSensitivityChanged
onValueChange = ::onSensitivityChanged
)
} else {
PreferenceText(title = "Rageshaking is not supported by your device")