diff --git a/android/Omnivore/app/build.gradle b/android/Omnivore/app/build.gradle
index 86c1239ca..cb1022569 100644
--- a/android/Omnivore/app/build.gradle
+++ b/android/Omnivore/app/build.gradle
@@ -128,6 +128,7 @@ dependencies {
implementation 'androidx.compose.material3:material3-window-size-class:1.0.0-alpha16'
implementation 'com.google.android.gms:play-services-auth:20.2.0'
+ implementation "com.google.accompanist:accompanist-systemuicontroller:0.25.1"
}
apollo {
diff --git a/android/Omnivore/app/src/main/AndroidManifest.xml b/android/Omnivore/app/src/main/AndroidManifest.xml
index ed213ee7f..bf65b39f7 100644
--- a/android/Omnivore/app/src/main/AndroidManifest.xml
+++ b/android/Omnivore/app/src/main/AndroidManifest.xml
@@ -29,7 +29,7 @@
@@ -38,26 +38,5 @@
-
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/MainActivity.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/MainActivity.kt
index fbdd52768..eed5b92be 100644
--- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/MainActivity.kt
+++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/MainActivity.kt
@@ -1,46 +1,17 @@
package app.omnivore.omnivore
-import android.annotation.SuppressLint
-import android.app.Activity
-import android.content.Context
import android.os.Bundle
import android.view.View
-import android.widget.Toast
import androidx.activity.ComponentActivity
-import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
-import androidx.activity.result.ActivityResult
-import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
-import androidx.compose.foundation.background
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.*
-import androidx.compose.foundation.text.KeyboardActions
-import androidx.compose.foundation.text.KeyboardOptions
-import androidx.compose.material3.*
-import androidx.compose.runtime.*
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalFocusManager
-import androidx.compose.ui.text.input.ImeAction
-import androidx.compose.ui.text.input.PasswordVisualTransformation
-import androidx.compose.ui.unit.dp
import app.omnivore.omnivore.ui.theme.OmnivoreTheme
-import androidx.compose.runtime.livedata.observeAsState
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
-import com.google.android.gms.auth.api.signin.GoogleSignIn
-import com.google.android.gms.auth.api.signin.GoogleSignInAccount
-import com.google.android.gms.auth.api.signin.GoogleSignInClient
-import com.google.android.gms.auth.api.signin.GoogleSignInOptions
-import com.google.android.gms.common.GoogleApiAvailability
-import com.google.android.gms.common.GooglePlayServicesUtil.isGooglePlayServicesAvailable
-import com.google.android.gms.tasks.Task
+import app.omnivore.omnivore.ui.auth.LoginViewModel
+import app.omnivore.omnivore.ui.root.RootView
import dagger.hilt.android.AndroidEntryPoint
-import kotlinx.coroutines.launch
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@@ -51,11 +22,7 @@ class MainActivity : ComponentActivity() {
setContent {
OmnivoreTheme {
- // A surface container using the 'background' color from the theme
- ScreenMain(viewModel = viewModel)
-// Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
-// WelcomeView(viewModel)
-// }
+ RootView(viewModel = viewModel)
}
}
@@ -69,170 +36,3 @@ class MainActivity : ComponentActivity() {
}
}
}
-
-@Composable
-fun WelcomeView(viewModel: LoginViewModel) {
- val hasAuthToken: Boolean by viewModel.hasAuthTokenLiveData.observeAsState(false)
-
- if (hasAuthToken) {
- LoggedInView(viewModel)
- } else {
- LoginView(viewModel)
- }
-}
-
-@Composable
-fun LoginView(viewModel: LoginViewModel) {
- val isGoogleAuthAvailable: Boolean = GoogleApiAvailability
- .getInstance()
- .isGooglePlayServicesAvailable(LocalContext.current) == 0
-
- Column(
- verticalArrangement = Arrangement.Center,
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier = Modifier
- .background(MaterialTheme.colorScheme.background)
- ) {
- if (isGoogleAuthAvailable) {
- GoogleAuthButton(viewModel)
- }
- EmailLoginView(viewModel)
- }
-}
-
-@Composable
-fun LoggedInView(viewModel: LoginViewModel) {
- Column(
- verticalArrangement = Arrangement.Center,
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier = Modifier
- .background(MaterialTheme.colorScheme.background)
- .fillMaxSize()
- ) {
- Text("You have a valid auth token. Nice. Go save something in Chrome!")
-
- Button(onClick = {
- viewModel.logout()
- }) {
- Text(text = "Logout")
- }
- }
-}
-
-@SuppressLint("CoroutineCreationDuringComposition")
-@Composable
-fun EmailLoginView(viewModel: LoginViewModel) {
- var email by rememberSaveable { mutableStateOf("") }
- var password by rememberSaveable { mutableStateOf("") }
- val focusManager = LocalFocusManager.current
- val snackBarHostState = remember { SnackbarHostState() }
- val coroutineScope = rememberCoroutineScope()
-
- Column(
- verticalArrangement = Arrangement.Center,
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier = Modifier
- .background(MaterialTheme.colorScheme.background)
- .fillMaxSize()
- .clickable { focusManager.clearFocus() }
- ) {
- LoginFields(
- email,
- password,
- onEmailChange = { email = it },
- onPasswordChange = { password = it },
- onLoginClick = { viewModel.login(email, password) }
- )
-
- // TODO: add a activity indicator (maybe after a delay?)
- if (viewModel.isLoading) {
- Text("Loading...")
- }
-
- if (viewModel.errorMessage != null) {
- coroutineScope.launch {
- val result = snackBarHostState
- .showSnackbar(
- viewModel.errorMessage!!,
- actionLabel = "Dismiss",
- duration = SnackbarDuration.Indefinite
- )
- when (result) {
- SnackbarResult.ActionPerformed -> viewModel.resetErrorMessage()
- }
- }
-
- SnackbarHost(hostState = snackBarHostState)
- }
- }
-}
-
-@OptIn(ExperimentalMaterial3Api::class)
-@Composable
-fun LoginFields(
- email: String,
- password: String,
- onEmailChange: (String) -> Unit,
- onPasswordChange: (String) -> Unit,
- onLoginClick: () -> Unit
-) {
- val context = LocalContext.current
- val focusManager = LocalFocusManager.current
-
- Column(
- modifier = Modifier
- .fillMaxWidth()
- .height(300.dp),
- verticalArrangement = Arrangement.spacedBy(25.dp),
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
- Text("Never miss a great read")
-
- OutlinedTextField(
- value = email,
- placeholder = { Text(text = "user@email.com") },
- label = { Text(text = "email") },
- onValueChange = onEmailChange,
- keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
- keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() })
- )
-
- OutlinedTextField(
- value = password,
- placeholder = { Text(text = "password") },
- label = { Text(text = "password") },
- onValueChange = onPasswordChange,
- visualTransformation = PasswordVisualTransformation(),
- keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
- keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() })
- )
-
- Button(onClick = {
- if (email.isNotBlank() && password.isNotBlank()) {
- onLoginClick()
- focusManager.clearFocus()
- } else {
- Toast.makeText(
- context,
- "Please enter an email address and password.",
- Toast.LENGTH_SHORT
- ).show()
- }
- }) {
- Text(text = "Login")
- }
- }
-}
-
-//@Preview(
-// uiMode = Configuration.UI_MODE_NIGHT_YES,
-// showBackground = true,
-// name = "Dark Mode"
-//)
-//@Preview(showBackground = true)
-//@Composable
-//fun DefaultPreview() {
-// OmnivoreTheme {
-// LoginView()
-// }
-//}
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Routes.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Routes.kt
index c6ca342c8..47f70b2b3 100644
--- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Routes.kt
+++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Routes.kt
@@ -1,6 +1,7 @@
package app.omnivore.omnivore
sealed class Routes(val route: String) {
- object Splash : Routes("Splash")
- object EmailLogin : Routes("EmailLogin")
+ object Root : Routes("Root")
+ object Home : Routes("Home")
+ object Welcome : Routes("Welcome")
}
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ScreenMain.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ScreenMain.kt
deleted file mode 100644
index a2248f02e..000000000
--- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ScreenMain.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package app.omnivore.omnivore
-
-import androidx.compose.runtime.Composable
-import androidx.navigation.compose.NavHost
-import androidx.navigation.compose.composable
-import androidx.navigation.compose.rememberNavController
-import app.omnivore.omnivore.screen.EmailLoginPage
-import app.omnivore.omnivore.screen.SplashPage
-
-@Composable
-fun ScreenMain(viewModel: LoginViewModel){
- val navController = rememberNavController()
-
- NavHost(navController = navController, startDestination = Routes.Splash.route) {
- composable(Routes.Splash.route) {
- SplashPage(viewModel = viewModel, navController = navController)
- }
- composable(Routes.EmailLogin.route) {
- EmailLoginPage(viewModel = viewModel, navController = navController)
- }
- }
-}
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/screen/EmailLogin.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/screen/EmailLogin.kt
deleted file mode 100644
index 8d2784eb8..000000000
--- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/screen/EmailLogin.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-package app.omnivore.omnivore.screen
-
-import android.content.Intent
-import android.net.Uri
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.navigationBarsPadding
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.navigation.NavHostController
-import app.omnivore.omnivore.EmailLoginView
-import app.omnivore.omnivore.LoginViewModel
-
-
-@Composable
-fun EmailLoginPage(viewModel: LoginViewModel, navController: NavHostController) {
- Column(
- verticalArrangement = Arrangement.Center,
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier = Modifier
- .background(MaterialTheme.colorScheme.background)
- .fillMaxSize()
- .navigationBarsPadding()
- ) {
- EmailLoginView(viewModel)
- }
-}
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/screen/SplashPage.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/screen/SplashPage.kt
deleted file mode 100644
index 8e79e6930..000000000
--- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/screen/SplashPage.kt
+++ /dev/null
@@ -1,78 +0,0 @@
-package app.omnivore.omnivore.screen
-
-import android.content.Intent
-import android.net.Uri
-import androidx.compose.foundation.background
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.navigationBarsPadding
-import androidx.compose.foundation.text.ClickableText
-import androidx.compose.material3.Button
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalUriHandler
-import androidx.compose.ui.text.AnnotatedString
-import androidx.core.content.ContextCompat.startActivity
-import androidx.lifecycle.viewModelScope
-import androidx.navigation.NavHostController
-import app.omnivore.omnivore.EmailLoginView
-import app.omnivore.omnivore.GoogleAuthButton
-import app.omnivore.omnivore.LoginViewModel
-import app.omnivore.omnivore.Routes
-import com.google.android.gms.common.GoogleApiAvailability
-import kotlinx.coroutines.launch
-
-@Composable
-fun SplashPage(viewModel: LoginViewModel, navController: NavHostController) {
- val isGoogleAuthAvailable: Boolean = GoogleApiAvailability
- .getInstance()
- .isGooglePlayServicesAvailable(LocalContext.current) == 0
-
- Column(
- verticalArrangement = Arrangement.Center,
- horizontalAlignment = Alignment.CenterHorizontally,
- modifier = Modifier
- .background(MaterialTheme.colorScheme.background)
- .fillMaxSize()
- .navigationBarsPadding()
- ) {
- Text("Never miss a great read")
- MoreInfoButton()
-
- if (isGoogleAuthAvailable) {
- GoogleAuthButton(viewModel)
- }
-
- ContinueWithEmailButton(navController)
- }
-}
-
-@Composable
-fun MoreInfoButton() {
- val context = LocalContext.current
- val intent = remember { Intent(Intent.ACTION_VIEW, Uri.parse("https://omnivore.app/about")) }
-
- ClickableText(
- text = AnnotatedString("Learn More ->"),
- onClick = {
- context.startActivity(intent)
- }
- )
-}
-
-@Composable
-fun ContinueWithEmailButton(navController: NavHostController) {
- ClickableText(
- text = AnnotatedString("Continue with Email ->"),
- onClick = {
- navController.navigate(Routes.EmailLogin.route)
- }
- )
-}
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/EmailLogin.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/EmailLogin.kt
new file mode 100644
index 000000000..c30b53790
--- /dev/null
+++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/EmailLogin.kt
@@ -0,0 +1,110 @@
+package app.omnivore.omnivore.ui.auth
+
+import android.annotation.SuppressLint
+import android.widget.Toast
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.text.ClickableText
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material3.*
+import androidx.compose.runtime.*
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.PasswordVisualTransformation
+import androidx.compose.ui.unit.dp
+
+@SuppressLint("CoroutineCreationDuringComposition")
+@Composable
+fun EmailLoginView(viewModel: LoginViewModel, onAuthProviderButtonTap: () -> Unit) {
+ var email by rememberSaveable { mutableStateOf("") }
+ var password by rememberSaveable { mutableStateOf("") }
+
+ Row(
+ horizontalArrangement = Arrangement.Center
+ ) {
+ Spacer(modifier = Modifier.weight(1.0F))
+ Column(
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ LoginFields(
+ email,
+ password,
+ onEmailChange = { email = it },
+ onPasswordChange = { password = it },
+ onLoginClick = { viewModel.login(email, password) }
+ )
+
+ // TODO: add a activity indicator (maybe after a delay?)
+ if (viewModel.isLoading) {
+ Text("Loading...")
+ }
+
+ ClickableText(
+ text = AnnotatedString("Continue with Google/Apple ->"),
+ onClick = { onAuthProviderButtonTap() }
+ )
+ }
+ Spacer(modifier = Modifier.weight(1.0F))
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun LoginFields(
+ email: String,
+ password: String,
+ onEmailChange: (String) -> Unit,
+ onPasswordChange: (String) -> Unit,
+ onLoginClick: () -> Unit
+) {
+ val context = LocalContext.current
+ val focusManager = LocalFocusManager.current
+
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(300.dp),
+ verticalArrangement = Arrangement.spacedBy(25.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ OutlinedTextField(
+ value = email,
+ placeholder = { Text(text = "user@email.com") },
+ label = { Text(text = "email") },
+ onValueChange = onEmailChange,
+ keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
+ keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() })
+ )
+
+ OutlinedTextField(
+ value = password,
+ placeholder = { Text(text = "password") },
+ label = { Text(text = "password") },
+ onValueChange = onPasswordChange,
+ visualTransformation = PasswordVisualTransformation(),
+ keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
+ keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() })
+ )
+
+ Button(onClick = {
+ if (email.isNotBlank() && password.isNotBlank()) {
+ onLoginClick()
+ focusManager.clearFocus()
+ } else {
+ Toast.makeText(
+ context,
+ "Please enter an email address and password.",
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ }) {
+ Text(text = "Login")
+ }
+ }
+}
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/GoogleAuth.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/GoogleAuth.kt
similarity index 93%
rename from android/Omnivore/app/src/main/java/app/omnivore/omnivore/GoogleAuth.kt
rename to android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/GoogleAuth.kt
index ba8b9eae0..853787007 100644
--- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/GoogleAuth.kt
+++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/GoogleAuth.kt
@@ -1,16 +1,15 @@
-package app.omnivore.omnivore
+package app.omnivore.omnivore.ui.auth
import android.app.Activity
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.material.ExperimentalMaterialApi
-import androidx.compose.material3.Button
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
+import app.omnivore.omnivore.R
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
@@ -40,7 +39,7 @@ fun GoogleAuthButton(viewModel: LoginViewModel) {
}
}
- GoogleSignInButton(
+ LoadingButtonWithIcon(
text = "Continue with Google",
loadingText = "Signing in...",
isLoading = viewModel.isLoading,
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/GoogleSignInButton.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/LoadingButtonWithIcon.kt
similarity index 83%
rename from android/Omnivore/app/src/main/java/app/omnivore/omnivore/GoogleSignInButton.kt
rename to android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/LoadingButtonWithIcon.kt
index e80f8c0ce..03e273231 100644
--- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/GoogleSignInButton.kt
+++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/LoadingButtonWithIcon.kt
@@ -1,10 +1,9 @@
-package app.omnivore.omnivore
+package app.omnivore.omnivore.ui.auth
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
-import androidx.compose.material.*
-import androidx.compose.material.Surface
+import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -14,21 +13,21 @@ import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.unit.dp
-@ExperimentalMaterialApi
@Composable
-fun GoogleSignInButton(
+fun LoadingButtonWithIcon(
text: String,
- loadingText: String = "Signing in...",
+ loadingText: String,
icon: Painter,
isLoading: Boolean = false,
shape: Shape = Shapes().medium,
borderColor: Color = Color.LightGray,
- backgroundColor: Color = MaterialTheme.colors.surface,
- progressIndicatorColor: Color = MaterialTheme.colors.primary,
+ backgroundColor: Color = MaterialTheme.colorScheme.surface,
+ progressIndicatorColor: Color = MaterialTheme.colorScheme.primary,
+ modifier: Modifier = Modifier,
onClick: () -> Unit
) {
Surface(
- modifier = Modifier.clickable(
+ modifier = modifier.clickable(
enabled = !isLoading,
onClick = onClick
),
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/LoginViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/LoginViewModel.kt
similarity index 95%
rename from android/Omnivore/app/src/main/java/app/omnivore/omnivore/LoginViewModel.kt
rename to android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/LoginViewModel.kt
index 14db1873b..b9186ed13 100644
--- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/LoginViewModel.kt
+++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/LoginViewModel.kt
@@ -1,13 +1,12 @@
-package app.omnivore.omnivore
+package app.omnivore.omnivore.ui.auth
import android.content.ContentValues
import android.util.Log
-import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
-import androidx.compose.ui.platform.LocalUriHandler
import androidx.lifecycle.*
+import app.omnivore.omnivore.*
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.tasks.Task
@@ -16,6 +15,11 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
import javax.inject.Inject
+enum class RegistrationState {
+ AuthProviderButtons,
+ EmailSignIn
+}
+
@HiltViewModel
class LoginViewModel @Inject constructor(
private val datastoreRepo: DatastoreRepository
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/WelcomeScreen.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/WelcomeScreen.kt
new file mode 100644
index 000000000..dadfd961f
--- /dev/null
+++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/WelcomeScreen.kt
@@ -0,0 +1,165 @@
+package app.omnivore.omnivore.ui.auth
+
+import android.annotation.SuppressLint
+import android.content.Intent
+import android.net.Uri
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.text.ClickableText
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material3.*
+import androidx.compose.runtime.*
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.unit.dp
+import androidx.navigation.NavHostController
+import app.omnivore.omnivore.R
+import app.omnivore.omnivore.Routes
+import com.google.android.gms.common.GoogleApiAvailability
+import kotlinx.coroutines.launch
+
+@Composable
+fun WelcomeScreen(viewModel: LoginViewModel) {
+ Surface(modifier = Modifier.fillMaxSize(), color = Color(0xFFFBEAA8 )) {
+ WelcomeScreenContent(viewModel = viewModel)
+ }
+}
+
+@SuppressLint("CoroutineCreationDuringComposition")
+@Composable
+fun WelcomeScreenContent(viewModel: LoginViewModel) {
+ var registrationState by rememberSaveable { mutableStateOf(RegistrationState.AuthProviderButtons) }
+
+ val onRegistrationStateChange = { state: RegistrationState ->
+ registrationState = state
+ }
+
+ val snackBarHostState = remember { SnackbarHostState() }
+ val coroutineScope = rememberCoroutineScope()
+ val focusManager = LocalFocusManager.current
+
+ Column(
+ verticalArrangement = Arrangement.SpaceAround,
+ horizontalAlignment = Alignment.Start,
+ modifier = Modifier
+ .background(Color(0xFFFBEAA8))
+ .fillMaxSize()
+ .navigationBarsPadding()
+ .padding(horizontal = 16.dp)
+ .clickable { focusManager.clearFocus() }
+ ) {
+ Spacer(modifier = Modifier.height(50.dp))
+ Image(
+ painter = painterResource(id = R.drawable.ic_omnivore_name_logo),
+ contentDescription = "Omnivore Icon with Name"
+ )
+ Spacer(modifier = Modifier.height(50.dp))
+
+ when(registrationState) {
+ RegistrationState.EmailSignIn -> {
+ EmailLoginView(
+ viewModel = viewModel,
+ onAuthProviderButtonTap = {
+ onRegistrationStateChange(RegistrationState.AuthProviderButtons)
+ }
+ )
+ }
+ RegistrationState.AuthProviderButtons -> {
+ Text(
+ text = stringResource(id = R.string.welcome_title),
+ style = MaterialTheme.typography.headlineLarge
+ )
+
+ MoreInfoButton()
+
+ Spacer(modifier = Modifier.height(50.dp))
+
+ AuthProviderView(
+ viewModel = viewModel,
+ onEmailButtonTap = { onRegistrationStateChange(RegistrationState.EmailSignIn) }
+ )
+ }
+ }
+
+ Spacer(modifier = Modifier.weight(1.0F))
+ }
+
+ if (viewModel.errorMessage != null) {
+ coroutineScope.launch {
+ val result = snackBarHostState
+ .showSnackbar(
+ viewModel.errorMessage!!,
+ actionLabel = "Dismiss",
+ duration = SnackbarDuration.Indefinite
+ )
+ when (result) {
+ SnackbarResult.ActionPerformed -> viewModel.resetErrorMessage()
+ }
+ }
+
+ SnackbarHost(hostState = snackBarHostState)
+ }
+}
+
+@Composable
+fun AuthProviderView(
+ viewModel: LoginViewModel,
+ onEmailButtonTap: () -> Unit
+) {
+ val isGoogleAuthAvailable: Boolean = GoogleApiAvailability
+ .getInstance()
+ .isGooglePlayServicesAvailable(LocalContext.current) == 0
+
+ Row(
+ horizontalArrangement = Arrangement.Center
+ ) {
+ Spacer(modifier = Modifier.weight(1.0F))
+ Column(
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ if (isGoogleAuthAvailable) {
+ GoogleAuthButton(viewModel)
+ }
+
+ LoadingButtonWithIcon(
+ text = "Continue with Apple",
+ loadingText = "Signing in...",
+ icon = painterResource(id = R.drawable.ic_logo_apple),
+ modifier = Modifier.padding(vertical = 6.dp),
+ onClick = {}
+ )
+
+ ClickableText(
+ text = AnnotatedString("Continue with Email ->"),
+ onClick = { onEmailButtonTap() }
+ )
+ }
+ Spacer(modifier = Modifier.weight(1.0F))
+ }
+}
+
+@Composable
+fun MoreInfoButton() {
+ val context = LocalContext.current
+ val intent = remember { Intent(Intent.ACTION_VIEW, Uri.parse("https://omnivore.app/about")) }
+
+ ClickableText(
+ text = AnnotatedString("Learn More ->"),
+ style = MaterialTheme.typography.titleLarge,
+ onClick = {
+ context.startActivity(intent)
+ },
+ modifier = Modifier.padding(vertical = 6.dp)
+ )
+}
+
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt
new file mode 100644
index 000000000..11ce48e54
--- /dev/null
+++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt
@@ -0,0 +1,36 @@
+package app.omnivore.omnivore.ui.home
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Button
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.navigation.NavHostController
+import app.omnivore.omnivore.ui.auth.LoginViewModel
+
+@Composable
+fun HomeView(viewModel: LoginViewModel) {
+ Column(
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = Modifier
+ .background(MaterialTheme.colorScheme.background)
+ .fillMaxSize()
+ .padding(horizontal = 6.dp)
+ ) {
+ Text("You have a valid auth token. Nice. Go save something in Chrome!")
+
+ Button(onClick = {
+ viewModel.logout()
+ }) {
+ Text(text = "Logout")
+ }
+ }
+}
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/root/RootView.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/root/RootView.kt
new file mode 100644
index 000000000..9bdd36a04
--- /dev/null
+++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/root/RootView.kt
@@ -0,0 +1,19 @@
+package app.omnivore.omnivore.ui.root
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.livedata.observeAsState
+import app.omnivore.omnivore.ui.auth.LoginViewModel
+import app.omnivore.omnivore.ui.auth.WelcomeScreen
+import app.omnivore.omnivore.ui.home.HomeView
+
+@Composable
+fun RootView(viewModel: LoginViewModel) {
+ val hasAuthToken: Boolean by viewModel.hasAuthTokenLiveData.observeAsState(false)
+
+ if (hasAuthToken) {
+ HomeView(viewModel = viewModel)
+ } else {
+ WelcomeScreen(viewModel = viewModel)
+ }
+}
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/NewFlowActivity.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/save/NewFlowActivity.kt
similarity index 72%
rename from android/Omnivore/app/src/main/java/app/omnivore/omnivore/NewFlowActivity.kt
rename to android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/save/NewFlowActivity.kt
index 0748fda0f..571a622f8 100644
--- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/NewFlowActivity.kt
+++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/save/NewFlowActivity.kt
@@ -1,6 +1,7 @@
-package app.omnivore.omnivore
+package app.omnivore.omnivore.ui.save
import androidx.compose.material.ExperimentalMaterialApi
+import app.omnivore.omnivore.ui.save.SaveSheetActivity
// Not sure why we need this class, but directly opening SaveSheetActivity
// causes the app to crash.
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/SaveContent.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/save/SaveContent.kt
similarity index 96%
rename from android/Omnivore/app/src/main/java/app/omnivore/omnivore/SaveContent.kt
rename to android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/save/SaveContent.kt
index 4a569178b..62a355061 100644
--- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/SaveContent.kt
+++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/save/SaveContent.kt
@@ -8,9 +8,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.material.*
-import androidx.compose.material.MaterialTheme.colors
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.unit.dp
+import app.omnivore.omnivore.ui.save.SaveViewModel
import kotlinx.coroutines.launch
@Composable
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/SaveSheetActivity.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/save/SaveSheetActivity.kt
similarity index 98%
rename from android/Omnivore/app/src/main/java/app/omnivore/omnivore/SaveSheetActivity.kt
rename to android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/save/SaveSheetActivity.kt
index 521e5c3df..4ffc0fcae 100644
--- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/SaveSheetActivity.kt
+++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/save/SaveSheetActivity.kt
@@ -1,4 +1,4 @@
-package app.omnivore.omnivore
+package app.omnivore.omnivore.ui.save
import android.content.ContentValues
import android.content.Intent
@@ -18,6 +18,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
+import app.omnivore.omnivore.SaveContent
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/SaveViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/save/SaveViewModel.kt
similarity index 92%
rename from android/Omnivore/app/src/main/java/app/omnivore/omnivore/SaveViewModel.kt
rename to android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/save/SaveViewModel.kt
index 1cbf43078..710554047 100644
--- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/SaveViewModel.kt
+++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/save/SaveViewModel.kt
@@ -1,4 +1,4 @@
-package app.omnivore.omnivore
+package app.omnivore.omnivore.ui.save
import android.content.ContentValues
import android.util.Log
@@ -7,6 +7,9 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
+import app.omnivore.omnivore.Constants
+import app.omnivore.omnivore.DatastoreKeys
+import app.omnivore.omnivore.DatastoreRepository
import app.omnivore.omnivore.graphql.generated.SaveUrlMutation
import app.omnivore.omnivore.graphql.generated.type.SaveUrlInput
import com.apollographql.apollo3.ApolloClient
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/theme/Theme.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/theme/Theme.kt
index f55b9dea7..38c6bc295 100644
--- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/theme/Theme.kt
+++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/theme/Theme.kt
@@ -82,7 +82,7 @@ fun OmnivoreTheme(
MaterialTheme(
colorScheme = colorScheme,
-// typography = Typography,
+ typography = Typography,
// shapes = Shapes,
content = content
)
diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/theme/Type.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/theme/Type.kt
index 5a70fbaec..86df5fe88 100644
--- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/theme/Type.kt
+++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/theme/Type.kt
@@ -1,28 +1,25 @@
package app.omnivore.omnivore.ui.theme
-import androidx.compose.material.Typography
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.font.FontFamily
-import androidx.compose.ui.text.font.FontWeight
-import androidx.compose.ui.unit.sp
+import androidx.compose.material3.Typography
+
+val Typography = Typography()
+
+// ex: https://github.com/android/compose-samples/blob/main/Jetchat/app/src/main/java/com/example/compose/jetchat/theme/Typography.kt
+//val Typography = Typography(
+//displayLarge: TextStyle = TypographyTokens.DisplayLarge,
+//displayMedium: TextStyle = TypographyTokens.DisplayMedium,
+//displaySmall: TextStyle = TypographyTokens.DisplaySmall,
+//headlineLarge: TextStyle = TypographyTokens.HeadlineLarge,
+//headlineMedium: TextStyle = TypographyTokens.HeadlineMedium,
+//headlineSmall: TextStyle = TypographyTokens.HeadlineSmall,
+//titleLarge: TextStyle = TypographyTokens.TitleLarge,
+//titleMedium: TextStyle = TypographyTokens.TitleMedium,
+//titleSmall: TextStyle = TypographyTokens.TitleSmall,
+//bodyLarge: TextStyle = TypographyTokens.BodyLarge,
+//bodyMedium: TextStyle = TypographyTokens.BodyMedium,
+//bodySmall: TextStyle = TypographyTokens.BodySmall,
+//labelLarge: TextStyle = TypographyTokens.LabelLarge,
+//labelMedium: TextStyle = TypographyTokens.LabelMedium,
+//labelSmall: TextStyle = TypographyTokens.LabelSmall
+//)
-// Set of Material typography styles to start with
-val Typography = Typography(
- body1 = TextStyle(
- fontFamily = FontFamily.Default,
- fontWeight = FontWeight.Normal,
- fontSize = 16.sp
- )
- /* Other default text styles to override
- button = TextStyle(
- fontFamily = FontFamily.Default,
- fontWeight = FontWeight.W500,
- fontSize = 14.sp
- ),
- caption = TextStyle(
- fontFamily = FontFamily.Default,
- fontWeight = FontWeight.Normal,
- fontSize = 12.sp
- )
- */
-)
diff --git a/android/Omnivore/app/src/main/res/drawable/ic_logo_apple.xml b/android/Omnivore/app/src/main/res/drawable/ic_logo_apple.xml
new file mode 100644
index 000000000..84a25baf6
--- /dev/null
+++ b/android/Omnivore/app/src/main/res/drawable/ic_logo_apple.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/android/Omnivore/app/src/main/res/drawable/ic_omnivore_name_logo.xml b/android/Omnivore/app/src/main/res/drawable/ic_omnivore_name_logo.xml
new file mode 100644
index 000000000..3129b743c
--- /dev/null
+++ b/android/Omnivore/app/src/main/res/drawable/ic_omnivore_name_logo.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/Omnivore/app/src/main/res/values/strings.xml b/android/Omnivore/app/src/main/res/values/strings.xml
index 2f4570ea1..cf0a10906 100644
--- a/android/Omnivore/app/src/main/res/values/strings.xml
+++ b/android/Omnivore/app/src/main/res/values/strings.xml
@@ -1,4 +1,5 @@
Omnivore
590528454690-mpmbvcb4c3ifmnojmrultt37eo8f64at.apps.googleusercontent.com
+ Everything you read.\nSafe, organized, and\neasy to share.
diff --git a/android/Omnivore/app/src/main/res/values/themes.xml b/android/Omnivore/app/src/main/res/values/themes.xml
index f5b5ca333..fa82596de 100644
--- a/android/Omnivore/app/src/main/res/values/themes.xml
+++ b/android/Omnivore/app/src/main/res/values/themes.xml
@@ -1,8 +1,7 @@
-