diff --git a/.idea/misc.xml b/.idea/misc.xml
index 4015527..9079288 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -70,8 +70,15 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/java/com/pixelized/biblib/ui/MainActivity.kt b/app/src/main/java/com/pixelized/biblib/ui/MainActivity.kt
index 788bfa9..4ffd291 100644
--- a/app/src/main/java/com/pixelized/biblib/ui/MainActivity.kt
+++ b/app/src/main/java/com/pixelized/biblib/ui/MainActivity.kt
@@ -6,20 +6,21 @@ import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.tween
-import androidx.compose.material.MaterialTheme
-import androidx.compose.material.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
+import com.pixelized.biblib.ui.composable.screen.LoginScreenComposable
+import com.pixelized.biblib.ui.composable.screen.MainScreenComposable
+import com.pixelized.biblib.ui.composable.screen.SplashScreenComposable
import com.pixelized.biblib.ui.theme.BibLibTheme
+import com.pixelized.biblib.ui.viewmodel.AuthenticationViewModel
import com.pixelized.biblib.ui.viewmodel.NavigationViewModel
import com.pixelized.biblib.ui.viewmodel.NavigationViewModel.Screen
-import com.pixelized.biblib.ui.composable.screen.SplashScreenComposable
-import com.pixelized.biblib.ui.composable.screen.MainScreenComposable
import com.pixelized.biblib.utils.BitmapCache
class MainActivity : ComponentActivity() {
private val navigationViewModel: NavigationViewModel by viewModels()
+ private val authenticationViewModel: AuthenticationViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -28,7 +29,7 @@ class MainActivity : ComponentActivity() {
setContent {
BibLibTheme {
- ContentComposable(navigationViewModel)
+ ContentComposable(navigationViewModel, authenticationViewModel)
}
}
}
@@ -41,13 +42,20 @@ class MainActivity : ComponentActivity() {
}
@Composable
-fun ContentComposable(navigationViewModel: NavigationViewModel) {
+fun ContentComposable(
+ navigationViewModel: NavigationViewModel,
+ authenticationViewModel: AuthenticationViewModel
+) {
val main by navigationViewModel.screen.observeAsState()
Crossfade(targetState = main, animationSpec = tween(1000)) {
when (it) {
is Screen.SplashScreen -> SplashScreenComposable(navigationViewModel)
is Screen.MainScreen -> MainScreenComposable(navigationViewModel)
+ is Screen.LoginScreen -> LoginScreenComposable(
+ navigationViewModel = navigationViewModel,
+ authenticationViewModel = authenticationViewModel
+ )
}
}
}
diff --git a/app/src/main/java/com/pixelized/biblib/ui/composable/screen/LoginScreenComposable.kt b/app/src/main/java/com/pixelized/biblib/ui/composable/screen/LoginScreenComposable.kt
new file mode 100644
index 0000000..36edc22
--- /dev/null
+++ b/app/src/main/java/com/pixelized/biblib/ui/composable/screen/LoginScreenComposable.kt
@@ -0,0 +1,176 @@
+package com.pixelized.biblib.ui.composable.screen
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.*
+import androidx.compose.material.ButtonDefaults.textButtonColors
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.sharp.Visibility
+import androidx.compose.material.icons.sharp.VisibilityOff
+import androidx.compose.runtime.*
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.input.PasswordVisualTransformation
+import androidx.compose.ui.text.input.VisualTransformation
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.pixelized.biblib.R
+import com.pixelized.biblib.ui.theme.BibLibTheme
+import com.pixelized.biblib.ui.viewmodel.AuthenticationViewModel
+import com.pixelized.biblib.ui.viewmodel.NavigationViewModel
+
+@Preview
+@Composable
+fun LoginScreenComposablePreview() {
+ BibLibTheme {
+ val navigationViewModel = NavigationViewModel()
+ val authenticationViewModel = AuthenticationViewModel()
+ LoginScreenComposable(navigationViewModel, authenticationViewModel)
+ }
+}
+
+@Composable
+fun LoginScreenComposable(
+ navigationViewModel: NavigationViewModel,
+ authenticationViewModel: AuthenticationViewModel
+) {
+ val typography = MaterialTheme.typography
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .fillMaxHeight()
+ .verticalScroll(rememberScrollState())
+ .padding(16.dp)
+ ) {
+ Spacer(modifier = Modifier.weight(1f))
+
+ Text(
+ modifier = Modifier
+ .padding(vertical = 16.dp)
+ .align(alignment = Alignment.CenterHorizontally),
+ style = typography.h4,
+ text = stringResource(id = R.string.welcome_sign_in)
+ )
+
+ Spacer(modifier = Modifier.weight(1f))
+
+ LoginField(
+ viewModel = authenticationViewModel,
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(bottom = 16.dp)
+ )
+ PasswordField(
+ viewModel = authenticationViewModel,
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(bottom = 16.dp)
+ )
+ CredentialRemember(
+ viewModel = authenticationViewModel,
+ modifier = Modifier
+ .height(48.dp)
+ .padding(bottom = 16.dp)
+ )
+ Row(
+ modifier = Modifier
+ .padding(bottom = 16.dp)
+ .align(Alignment.End)
+ ) {
+ Button(
+ modifier = Modifier.padding(end = 8.dp),
+ colors = textButtonColors(),
+ onClick = {
+ // TODO:
+ navigationViewModel.navigateTo(NavigationViewModel.Screen.MainScreen)
+ }) {
+ Text(text = stringResource(id = R.string.action_register))
+ }
+ Button(onClick = {
+ // TODO:
+ navigationViewModel.navigateTo(NavigationViewModel.Screen.MainScreen)
+ }) {
+ Text(text = stringResource(id = R.string.action_login))
+ }
+ }
+
+ Spacer(modifier = Modifier.weight(2f))
+
+ Button(
+ modifier = Modifier.fillMaxWidth(),
+ colors = ButtonDefaults.outlinedButtonColors(),
+ onClick = {
+ // TODO:
+ navigationViewModel.navigateTo(NavigationViewModel.Screen.MainScreen)
+ }) {
+ Image(
+ modifier = Modifier.padding(end = 8.dp),
+ painter = painterResource(id = R.drawable.ic_google), contentDescription = ""
+ )
+ Text(text = stringResource(id = R.string.action_google_sign_in))
+ }
+ }
+}
+
+@Composable
+private fun LoginField(viewModel: AuthenticationViewModel, modifier: Modifier = Modifier) {
+ val login: State = viewModel.login.observeAsState()
+ TextField(
+ modifier = modifier,
+ value = login.value ?: "",
+ label = { Text(text = stringResource(id = R.string.authentication_login)) },
+ maxLines = 1,
+ singleLine = true,
+ onValueChange = { viewModel.updateLogin(it) },
+ )
+}
+
+@Composable
+private fun PasswordField(viewModel: AuthenticationViewModel, modifier: Modifier = Modifier) {
+ val password = viewModel.password.observeAsState()
+ var passwordVisibility by remember { mutableStateOf(false) }
+ TextField(
+ modifier = modifier,
+ value = password.value ?: "",
+ maxLines = 1,
+ singleLine = true,
+ visualTransformation = if (passwordVisibility) VisualTransformation.None else PasswordVisualTransformation(),
+ trailingIcon = {
+ IconButton(onClick = { passwordVisibility = passwordVisibility.not() }) {
+ Icon(
+ imageVector = if (passwordVisibility) Icons.Sharp.VisibilityOff else Icons.Sharp.Visibility,
+ contentDescription = "password visibility"
+ )
+ }
+ },
+ label = { Text(text = stringResource(id = R.string.authentication_password)) },
+ onValueChange = { viewModel.updatePassword(it) }
+ )
+}
+
+@Composable
+private fun CredentialRemember(viewModel: AuthenticationViewModel, modifier: Modifier = Modifier) {
+ val credential = viewModel.rememberCredential.observeAsState()
+ Row(modifier = modifier.clickable {
+ viewModel.updateRememberCredential(credential = credential.value?.not() ?: false)
+ }) {
+ Checkbox(
+ modifier = Modifier.align(Alignment.CenterVertically),
+ checked = credential.value ?: false,
+ onCheckedChange = null
+ )
+ Text(
+ modifier = Modifier
+ .align(Alignment.CenterVertically)
+ .padding(start = 8.dp),
+ text = stringResource(id = R.string.authentication_credential_remember)
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/pixelized/biblib/ui/composable/screen/SplashScreenComposable.kt b/app/src/main/java/com/pixelized/biblib/ui/composable/screen/SplashScreenComposable.kt
index 1a5e410..4bd79cd 100644
--- a/app/src/main/java/com/pixelized/biblib/ui/composable/screen/SplashScreenComposable.kt
+++ b/app/src/main/java/com/pixelized/biblib/ui/composable/screen/SplashScreenComposable.kt
@@ -6,11 +6,14 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.pixelized.biblib.ui.theme.BibLibTheme
import com.pixelized.biblib.ui.viewmodel.NavigationViewModel
+import kotlinx.coroutines.*
@Preview
@@ -36,4 +39,12 @@ fun SplashScreenComposable(navigationViewModel: NavigationViewModel) {
text = "Welcome to BibLib"
)
}
+
+ val coroutineScope = rememberCoroutineScope()
+ LaunchedEffect(key1 = "loading", block = {
+ coroutineScope.launch {
+ delay(1000)
+ navigationViewModel.navigateTo(NavigationViewModel.Screen.LoginScreen)
+ }
+ })
}
\ No newline at end of file
diff --git a/app/src/main/java/com/pixelized/biblib/ui/viewmodel/AuthenticationViewModel.kt b/app/src/main/java/com/pixelized/biblib/ui/viewmodel/AuthenticationViewModel.kt
new file mode 100644
index 0000000..1c2b63d
--- /dev/null
+++ b/app/src/main/java/com/pixelized/biblib/ui/viewmodel/AuthenticationViewModel.kt
@@ -0,0 +1,29 @@
+package com.pixelized.biblib.ui.viewmodel
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+
+class AuthenticationViewModel: ViewModel() {
+
+ private val _login = MutableLiveData()
+ val login: LiveData get() = _login
+
+ private val _password = MutableLiveData()
+ val password: LiveData get() = _password
+
+ private val _rememberCredential = MutableLiveData(false)
+ val rememberCredential: LiveData get() = _rememberCredential
+
+ fun updateLogin(login: String) {
+ _login.postValue(login)
+ }
+
+ fun updatePassword(password: String) {
+ _password.postValue(password)
+ }
+
+ fun updateRememberCredential(credential: Boolean) {
+ _rememberCredential.postValue(credential)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/pixelized/biblib/ui/viewmodel/NavigationViewModel.kt b/app/src/main/java/com/pixelized/biblib/ui/viewmodel/NavigationViewModel.kt
index f177b27..c3e544b 100644
--- a/app/src/main/java/com/pixelized/biblib/ui/viewmodel/NavigationViewModel.kt
+++ b/app/src/main/java/com/pixelized/biblib/ui/viewmodel/NavigationViewModel.kt
@@ -13,21 +13,12 @@ class NavigationViewModel : ViewModel() {
private val stack = Stack()
- private val _screen = MutableLiveData()
+ private val _screen = MutableLiveData(Screen.SplashScreen)
val screen: LiveData get() = _screen
- private val _page = MutableLiveData()
+ private val _page = MutableLiveData(Page.HomePage)
val page: LiveData get() = _page
- init {
- _screen.value = Screen.SplashScreen
- viewModelScope.launch {
- delay(3000)
- navigateTo(Page.HomePage)
- navigateTo(Screen.MainScreen)
- }
- }
-
fun navigateTo(screen: Screen): Boolean {
_screen.postValue(screen)
return true
@@ -52,6 +43,7 @@ class NavigationViewModel : ViewModel() {
sealed class Screen {
object SplashScreen : Screen()
object MainScreen : Screen()
+ object LoginScreen : Screen()
}
sealed class Page {
diff --git a/app/src/main/res/drawable/ic_google.xml b/app/src/main/res/drawable/ic_google.xml
new file mode 100644
index 0000000..5c1b1b1
--- /dev/null
+++ b/app/src/main/res/drawable/ic_google.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4d36642..45c5ab1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,6 +1,16 @@
BibLib
+ Register
+ Login
+ Sign in with Google
+
+ Sign in to BibLib
+
+ Login
+ Password
+ Remember my credential
+
Rating
Language
Release