mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-19 19:58:26 +00:00
Make loading animation work when animations are off
This commit is contained in:
parent
eeabd32da4
commit
e139afed6a
@ -1,15 +1,10 @@
|
||||
package org.thoughtcrime.securesms.onboarding.loading
|
||||
|
||||
import androidx.compose.animation.core.Animatable
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.TweenSpec
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@ -21,24 +16,11 @@ import org.thoughtcrime.securesms.ui.contentDescription
|
||||
import org.thoughtcrime.securesms.ui.h7
|
||||
|
||||
@Composable
|
||||
internal fun LoadingScreen(state: State) {
|
||||
val animatable = remember { Animatable(initialValue = 0f, visibilityThreshold = 0.005f) }
|
||||
|
||||
LaunchedEffect(state) {
|
||||
animatable.stop()
|
||||
animatable.animateTo(
|
||||
targetValue = 1f,
|
||||
animationSpec = TweenSpec(
|
||||
durationMillis = state.duration.inWholeMilliseconds.toInt(),
|
||||
easing = LinearEasing
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
internal fun LoadingScreen(progress: Float) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
ProgressArc(
|
||||
animatable.value,
|
||||
progress,
|
||||
modifier = Modifier.contentDescription(R.string.AccessibilityId_loading_animation)
|
||||
)
|
||||
Text(
|
||||
|
@ -47,8 +47,8 @@ class LoadingActivity: BaseActionBarActivity() {
|
||||
ApplicationContext.getInstance(this).newAccount = false
|
||||
|
||||
setComposeContent {
|
||||
val state by viewModel.states.collectAsState()
|
||||
LoadingScreen(state)
|
||||
val progress by viewModel.progress.collectAsState()
|
||||
LoadingScreen(progress)
|
||||
}
|
||||
|
||||
lifecycleScope.launch {
|
||||
|
@ -4,15 +4,20 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.buffer
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.timeout
|
||||
import kotlinx.coroutines.launch
|
||||
@ -23,25 +28,43 @@ import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
data class State(val duration: Duration)
|
||||
enum class State {
|
||||
LOADING,
|
||||
SUCCESS,
|
||||
FAIL
|
||||
}
|
||||
|
||||
private val ANIMATE_TO_DONE_TIME = 500.milliseconds
|
||||
private val IDLE_DONE_TIME = 1.seconds
|
||||
private val TIMEOUT_TIME = 15.seconds
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
private val REFRESH_TIME = 50.milliseconds
|
||||
|
||||
@OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class)
|
||||
@HiltViewModel
|
||||
internal class LoadingViewModel @Inject constructor(
|
||||
val prefs: TextSecurePreferences
|
||||
): ViewModel() {
|
||||
|
||||
private val _states = MutableStateFlow(State(TIMEOUT_TIME))
|
||||
val states = _states.asStateFlow()
|
||||
private val state = MutableStateFlow(State.LOADING)
|
||||
|
||||
private val _progress = MutableStateFlow(0f)
|
||||
val progress = _progress.asStateFlow()
|
||||
|
||||
private val _events = MutableSharedFlow<Event>()
|
||||
val events = _events.asSharedFlow()
|
||||
|
||||
init {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
state.flatMapLatest {
|
||||
when (it) {
|
||||
State.LOADING -> progress(0f, 1f, TIMEOUT_TIME)
|
||||
else -> progress(progress.value, 1f, ANIMATE_TO_DONE_TIME)
|
||||
}
|
||||
}.buffer(0, BufferOverflow.DROP_OLDEST)
|
||||
.collectLatest { _progress.value = it }
|
||||
}
|
||||
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
TextSecurePreferences.events
|
||||
@ -58,7 +81,7 @@ internal class LoadingViewModel @Inject constructor(
|
||||
|
||||
private suspend fun onSuccess() {
|
||||
withContext(Dispatchers.Main) {
|
||||
_states.value = State(ANIMATE_TO_DONE_TIME)
|
||||
state.value = State.SUCCESS
|
||||
delay(IDLE_DONE_TIME)
|
||||
_events.emit(Event.SUCCESS)
|
||||
}
|
||||
@ -66,6 +89,8 @@ internal class LoadingViewModel @Inject constructor(
|
||||
|
||||
private suspend fun onFail() {
|
||||
withContext(Dispatchers.Main) {
|
||||
state.value = State.FAIL
|
||||
delay(IDLE_DONE_TIME)
|
||||
_events.emit(Event.TIMEOUT)
|
||||
}
|
||||
}
|
||||
@ -75,3 +100,22 @@ sealed interface Event {
|
||||
object SUCCESS: Event
|
||||
object TIMEOUT: Event
|
||||
}
|
||||
|
||||
private fun progress(
|
||||
init: Float,
|
||||
target: Float,
|
||||
time: Duration,
|
||||
refreshRate: Duration = REFRESH_TIME
|
||||
): Flow<Float> = flow {
|
||||
val startMs = System.currentTimeMillis()
|
||||
val timeMs = time.inWholeMilliseconds
|
||||
val finishMs = startMs + timeMs
|
||||
val range = target - init
|
||||
|
||||
generateSequence { System.currentTimeMillis() }.takeWhile { it < finishMs }.forEach {
|
||||
emit((it - startMs) * range / timeMs + init)
|
||||
delay(refreshRate)
|
||||
}
|
||||
|
||||
emit(target)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user