SplashScreen animation.
This commit is contained in:
parent
fa2af6dd90
commit
9cde3f6404
7 changed files with 181 additions and 50 deletions
|
|
@ -4,39 +4,47 @@ import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import com.pixelized.biblib.ui.viewmodel.authentication.AuthenticationViewModel
|
|
||||||
|
|
||||||
class CredentialRepository(application: Application) : ICredentialRepository {
|
class CredentialRepository(application: Application) : ICredentialRepository {
|
||||||
private val preferences =
|
private val preferences = application.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE)
|
||||||
application.getSharedPreferences(AuthenticationViewModel.SHARED_PREF, Context.MODE_PRIVATE)
|
|
||||||
|
|
||||||
override var login
|
override var login
|
||||||
get() = preferences.login
|
get() = preferences.login
|
||||||
set(value) {
|
set(value) = value.let { preferences.login = it }
|
||||||
preferences.login = value
|
|
||||||
}
|
|
||||||
|
|
||||||
override var password: String?
|
override var password: String?
|
||||||
get() = preferences.password
|
get() = preferences.password
|
||||||
set(value) {
|
set(value) = value.let { preferences.password = it }
|
||||||
preferences.password = value
|
|
||||||
}
|
|
||||||
|
|
||||||
override var rememberCredential: Boolean
|
override var rememberCredential: Boolean
|
||||||
get() = preferences.rememberCredential
|
get() = preferences.rememberCredential
|
||||||
set(value) {
|
set(value) = value.let { preferences.rememberCredential = it }
|
||||||
preferences.rememberCredential = value
|
|
||||||
}
|
override var bearer: String?
|
||||||
|
get() = preferences.bearer
|
||||||
|
set(value) = value.let { preferences.bearer = it }
|
||||||
|
|
||||||
private var SharedPreferences.login: String?
|
private var SharedPreferences.login: String?
|
||||||
get() = getString(AuthenticationViewModel.REMEMBER_USER, null)
|
get() = getString(REMEMBER_USER, null)
|
||||||
set(value) = edit { putString(AuthenticationViewModel.REMEMBER_USER, value) }
|
set(value) = edit { putString(REMEMBER_USER, value) }
|
||||||
|
|
||||||
private var SharedPreferences.password: String?
|
private var SharedPreferences.password: String?
|
||||||
get() = getString(AuthenticationViewModel.REMEMBER_PASSWORD, null)
|
get() = getString(REMEMBER_PASSWORD, null)
|
||||||
set(value) = edit { putString(AuthenticationViewModel.REMEMBER_PASSWORD, value) }
|
set(value) = edit { putString(REMEMBER_PASSWORD, value) }
|
||||||
|
|
||||||
private var SharedPreferences.rememberCredential: Boolean
|
private var SharedPreferences.rememberCredential: Boolean
|
||||||
get() = getBoolean(AuthenticationViewModel.REMEMBER_CREDENTIAL, false)
|
get() = getBoolean(REMEMBER_CREDENTIAL, false)
|
||||||
set(value) = edit { putBoolean(AuthenticationViewModel.REMEMBER_CREDENTIAL, value) }
|
set(value) = edit { putBoolean(REMEMBER_CREDENTIAL, value) }
|
||||||
|
|
||||||
|
private var SharedPreferences.bearer: String?
|
||||||
|
get() = getString(BEARER_TOKEN, null)
|
||||||
|
set(value) = edit { putString(BEARER_TOKEN, value) }
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val SHARED_PREF = "BIB_LIB_SHARED_PREF"
|
||||||
|
private const val REMEMBER_CREDENTIAL = "REMEMBER_CREDENTIAL"
|
||||||
|
private const val REMEMBER_USER = "REMEMBER_USER"
|
||||||
|
private const val REMEMBER_PASSWORD = "REMEMBER_PASSWORD"
|
||||||
|
private const val BEARER_TOKEN = "BEARER_TOKEN"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,4 +4,5 @@ interface ICredentialRepository {
|
||||||
var login: String?
|
var login: String?
|
||||||
var password: String?
|
var password: String?
|
||||||
var rememberCredential: Boolean
|
var rememberCredential: Boolean
|
||||||
|
var bearer: String?
|
||||||
}
|
}
|
||||||
|
|
@ -1,55 +1,121 @@
|
||||||
package com.pixelized.biblib.ui.composable.screen
|
package com.pixelized.biblib.ui.composable.screen
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.animation.*
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.pixelized.biblib.BuildConfig
|
||||||
|
import com.pixelized.biblib.R
|
||||||
import com.pixelized.biblib.ui.theme.BibLibTheme
|
import com.pixelized.biblib.ui.theme.BibLibTheme
|
||||||
|
import com.pixelized.biblib.ui.viewmodel.initialisation.IInitialisation
|
||||||
|
import com.pixelized.biblib.ui.viewmodel.initialisation.InitialisationViewModel
|
||||||
import com.pixelized.biblib.ui.viewmodel.navigation.INavigation
|
import com.pixelized.biblib.ui.viewmodel.navigation.INavigation
|
||||||
import com.pixelized.biblib.ui.viewmodel.navigation.NavigationViewModel
|
import com.pixelized.biblib.ui.viewmodel.navigation.NavigationViewModel
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun SplashScreenComposablePreview() {
|
fun SplashScreenComposablePreview() {
|
||||||
BibLibTheme {
|
BibLibTheme {
|
||||||
SplashScreenComposable(INavigation.Mock())
|
SplashScreenComposable(IInitialisation.Mock(), INavigation.Mock())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalAnimationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun SplashScreenComposable(
|
fun SplashScreenComposable(
|
||||||
|
initialisation: IInitialisation = viewModel<InitialisationViewModel>(),
|
||||||
navigation: INavigation = viewModel<NavigationViewModel>()
|
navigation: INavigation = viewModel<NavigationViewModel>()
|
||||||
) {
|
) {
|
||||||
|
val duration = 1000
|
||||||
val typography = MaterialTheme.typography
|
val typography = MaterialTheme.typography
|
||||||
|
initialisation.LoadApplication {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxHeight()
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(16.dp),
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(240.dp)
|
||||||
|
.align(Alignment.Center)
|
||||||
|
) {
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = it != IInitialisation.State.Finished,
|
||||||
|
initiallyVisible = false,
|
||||||
|
enter = fadeIn(animationSpec = tween(duration))
|
||||||
|
+ slideInVertically(
|
||||||
|
initialOffsetY = { height -> -height },
|
||||||
|
animationSpec = tween(duration)
|
||||||
|
),
|
||||||
|
exit = fadeOut(animationSpec = tween(duration))
|
||||||
|
+ slideOutVertically(
|
||||||
|
targetOffsetY = { height -> -height },
|
||||||
|
animationSpec = tween(duration)
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
style = typography.h4,
|
||||||
|
text = "Welcome to"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
AnimatedVisibility(
|
||||||
|
modifier = Modifier.align(Alignment.End),
|
||||||
|
visible = it != IInitialisation.State.Finished,
|
||||||
|
initiallyVisible = false,
|
||||||
|
enter = fadeIn(animationSpec = tween(duration))
|
||||||
|
+ slideInVertically(
|
||||||
|
initialOffsetY = { height -> height },
|
||||||
|
animationSpec = tween(duration)
|
||||||
|
),
|
||||||
|
exit = fadeOut(animationSpec = tween(duration))
|
||||||
|
+ slideOutVertically(
|
||||||
|
targetOffsetY = { height -> height },
|
||||||
|
animationSpec = tween(duration)
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
style = typography.h4,
|
||||||
|
text = stringResource(id = R.string.app_name)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Box(
|
AnimatedVisibility(
|
||||||
modifier = Modifier
|
modifier = Modifier.align(Alignment.BottomEnd),
|
||||||
.fillMaxHeight()
|
visible = it != IInitialisation.State.Finished,
|
||||||
.fillMaxWidth(),
|
enter = fadeIn(animationSpec = tween(duration)),
|
||||||
contentAlignment = Alignment.Center,
|
exit = fadeOut(animationSpec = tween(duration)),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
style = typography.h4,
|
style = typography.caption,
|
||||||
text = "Welcome to BibLib"
|
text = stringResource(
|
||||||
)
|
R.string.app_version,
|
||||||
}
|
BuildConfig.BUILD_TYPE.toUpperCase(Locale.getDefault()),
|
||||||
|
BuildConfig.VERSION_NAME,
|
||||||
val coroutineScope = rememberCoroutineScope()
|
BuildConfig.VERSION_CODE
|
||||||
LaunchedEffect(key1 = "loading", block = {
|
)
|
||||||
coroutineScope.launch {
|
)
|
||||||
delay(1000)
|
}
|
||||||
navigation.navigateTo(INavigation.Screen.LoginScreen)
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
if (it == IInitialisation.State.Finished) {
|
||||||
|
LaunchedEffect(key1 = "navigateTo(INavigation.Screen.LoginScreen)") {
|
||||||
|
delay(1000)
|
||||||
|
navigation.navigateTo(INavigation.Screen.LoginScreen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,11 +124,4 @@ class AuthenticationViewModel : ViewModel(), IAuthentication {
|
||||||
_state.postValue(State.Loading)
|
_state.postValue(State.Loading)
|
||||||
launcher?.launch(googleSignIn.client.signInIntent)
|
launcher?.launch(googleSignIn.client.signInIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val SHARED_PREF = "BIB_LIB_SHARED_PREF"
|
|
||||||
const val REMEMBER_CREDENTIAL = "REMEMBER_CREDENTIAL"
|
|
||||||
const val REMEMBER_USER = "REMEMBER_USER"
|
|
||||||
const val REMEMBER_PASSWORD = "REMEMBER_PASSWORD"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.pixelized.biblib.ui.viewmodel.initialisation
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
|
||||||
|
interface IInitialisation {
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LoadApplication(content: @Composable (State) -> Unit)
|
||||||
|
|
||||||
|
sealed class State {
|
||||||
|
abstract fun proceed(): State
|
||||||
|
|
||||||
|
object Initial : State() {
|
||||||
|
override fun proceed() = Loading
|
||||||
|
}
|
||||||
|
object Loading : State() {
|
||||||
|
override fun proceed() = Finished
|
||||||
|
}
|
||||||
|
object Finished : State() {
|
||||||
|
override fun proceed() = Finished
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Mock(private val state: State = State.Loading) : IInitialisation {
|
||||||
|
@Composable
|
||||||
|
override fun LoadApplication(content: (State) -> Unit) = content(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.pixelized.biblib.ui.viewmodel.initialisation
|
||||||
|
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.pixelized.biblib.network.client.IBibLibClient
|
||||||
|
import com.pixelized.biblib.repository.credential.ICredentialRepository
|
||||||
|
import com.pixelized.biblib.ui.viewmodel.initialisation.IInitialisation.State.*
|
||||||
|
import com.pixelized.biblib.utils.injection.inject
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
|
class InitialisationViewModel : ViewModel(), IInitialisation {
|
||||||
|
private val credentialRepository: ICredentialRepository by inject()
|
||||||
|
private val client: IBibLibClient by inject()
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
override fun LoadApplication(content: @Composable (IInitialisation.State) -> Unit) {
|
||||||
|
val state: MutableState<IInitialisation.State> = remember { mutableStateOf(Initial) }
|
||||||
|
|
||||||
|
LaunchedEffect(key1 = "LoadApplication") {
|
||||||
|
state.value = Loading
|
||||||
|
delay(2000)
|
||||||
|
|
||||||
|
val bearerToken = credentialRepository.bearer
|
||||||
|
if (bearerToken != null) {
|
||||||
|
client.updateBearerToken(bearerToken)
|
||||||
|
state.value = Finished
|
||||||
|
} else {
|
||||||
|
state.value = Finished
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
content(state.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">BibLib</string>
|
<string name="app_name">BibLib</string>
|
||||||
|
<string name="app_version">%1$s: %2$s - %3$d</string>
|
||||||
|
|
||||||
<string name="action_register">Register</string>
|
<string name="action_register">Register</string>
|
||||||
<string name="action_login">Login</string>
|
<string name="action_login">Login</string>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue