mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Merge pull request #1118 from omnivore-app/feature/android-login-screens
Draft: Android Welcome Screen
This commit is contained in:
commit
76ac1fd3c0
25 changed files with 435 additions and 407 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".NewFlowActivity"
|
||||
android:name=".ui.save.NewFlowActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.AppCompat.Translucent">
|
||||
<intent-filter>
|
||||
|
|
@ -38,26 +38,5 @@
|
|||
<data android:mimeType="text/*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!--
|
||||
<activity
|
||||
android:name=".SaveActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="image/*" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="application/pdf" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="text/*" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
-->
|
||||
</application>
|
||||
</manifest>
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
// }
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
@ -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
|
||||
),
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -82,7 +82,7 @@ fun OmnivoreTheme(
|
|||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
// typography = Typography,
|
||||
typography = Typography,
|
||||
// shapes = Shapes,
|
||||
content = content
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
*/
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M21.281,18.424C20.933,19.228 20.521,19.967 20.044,20.647C19.394,21.574 18.861,22.216 18.451,22.572C17.815,23.157 17.134,23.457 16.404,23.474C15.88,23.474 15.248,23.325 14.513,23.022C13.775,22.721 13.097,22.572 12.477,22.572C11.827,22.572 11.129,22.721 10.383,23.022C9.636,23.325 9.035,23.482 8.574,23.498C7.875,23.528 7.177,23.22 6.481,22.572C6.037,22.185 5.481,21.52 4.815,20.579C4.101,19.574 3.513,18.408 3.053,17.08C2.56,15.644 2.313,14.254 2.313,12.909C2.313,11.367 2.646,10.038 3.313,8.924C3.838,8.029 4.535,7.323 5.408,6.805C6.281,6.287 7.224,6.023 8.24,6.006C8.796,6.006 9.525,6.178 10.431,6.516C11.334,6.855 11.914,7.027 12.168,7.027C12.358,7.027 13.002,6.826 14.095,6.425C15.127,6.053 15.999,5.899 16.713,5.96C18.648,6.116 20.102,6.879 21.069,8.253C19.338,9.302 18.482,10.77 18.499,12.654C18.515,14.122 19.047,15.343 20.094,16.313C20.568,16.763 21.097,17.111 21.687,17.358C21.559,17.728 21.424,18.083 21.281,18.424ZM16.843,0.96C16.843,2.11 16.422,3.184 15.585,4.178C14.574,5.36 13.351,6.043 12.025,5.935C12.008,5.797 11.999,5.652 11.999,5.5C11.999,4.395 12.479,3.213 13.333,2.247C13.759,1.758 14.301,1.351 14.959,1.027C15.615,0.707 16.235,0.53 16.818,0.5C16.835,0.654 16.843,0.808 16.843,0.96V0.96Z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="129dp"
|
||||
android:height="26dp"
|
||||
android:viewportWidth="129"
|
||||
android:viewportHeight="26">
|
||||
<path
|
||||
android:pathData="M43.71,13.11C43.71,9.58 41.52,7.57 38.66,7.57C35.78,7.57 33.61,9.58 33.61,13.11C33.61,16.63 35.78,18.66 38.66,18.66C41.52,18.66 43.71,16.64 43.71,13.11ZM41.4,13.11C41.4,15.4 40.31,16.64 38.66,16.64C37,16.64 35.92,15.4 35.92,13.11C35.92,10.83 37,9.59 38.66,9.59C40.31,9.59 41.4,10.83 41.4,13.11Z"
|
||||
android:fillColor="#3D3D3D"/>
|
||||
<path
|
||||
android:pathData="M47.63,7.72V18.51H49.84V11.46H49.93L52.72,18.46H54.23L57.02,11.49H57.11V18.51H59.32V7.72H56.51L53.54,14.97H53.41L50.44,7.72H47.63Z"
|
||||
android:fillColor="#3D3D3D"/>
|
||||
<path
|
||||
android:pathData="M72.45,7.72H70.18V14.5H70.09L65.43,7.72H63.43V18.51H65.71V11.72H65.79L70.48,18.51H72.45V7.72Z"
|
||||
android:fillColor="#3D3D3D"/>
|
||||
<path
|
||||
android:pathData="M78.84,7.72H76.56V18.51H78.84V7.72Z"
|
||||
android:fillColor="#3D3D3D"/>
|
||||
<path
|
||||
android:pathData="M84.9,7.72H82.36L86.09,18.51H89.03L92.75,7.72H90.22L87.6,15.92H87.5L84.9,7.72Z"
|
||||
android:fillColor="#3D3D3D"/>
|
||||
<path
|
||||
android:pathData="M105.58,13.11C105.58,9.58 103.39,7.57 100.52,7.57C97.65,7.57 95.47,9.58 95.47,13.11C95.47,16.63 97.65,18.66 100.52,18.66C103.39,18.66 105.58,16.64 105.58,13.11ZM103.26,13.11C103.26,15.4 102.18,16.64 100.52,16.64C98.86,16.64 97.78,15.4 97.78,13.11C97.78,10.83 98.86,9.59 100.52,9.59C102.18,9.59 103.26,10.83 103.26,13.11Z"
|
||||
android:fillColor="#3D3D3D"/>
|
||||
<path
|
||||
android:pathData="M109.49,18.51H111.77V14.68H113.44L115.48,18.51H118L115.71,14.31C116.94,13.79 117.62,12.72 117.62,11.24C117.62,9.1 116.2,7.72 113.75,7.72H109.49V18.51ZM111.77,12.85V9.58H113.31C114.63,9.58 115.27,10.17 115.27,11.24C115.27,12.31 114.63,12.85 113.32,12.85H111.77Z"
|
||||
android:fillColor="#3D3D3D"/>
|
||||
<path
|
||||
android:pathData="M121.45,18.51H128.75V16.63H123.74V14.05H128.35V12.17H123.74V9.6H128.73V7.72H121.45V18.51Z"
|
||||
android:fillColor="#3D3D3D"/>
|
||||
<path
|
||||
android:pathData="M8.72,17.73V10.37C8.72,9.74 9.46,9.39 9.95,9.82L12.22,13.14C12.69,13.52 13.34,13.52 13.81,13.14L16.03,9.85C16.52,9.44 17.26,9.77 17.26,10.4V14.29C17.26,16.17 18.52,17.71 20.41,17.71H20.46C22.07,17.71 23.47,16.61 23.85,15.05C24.04,14.23 24.21,13.38 24.21,12.73C24.18,6.29 18.74,1.42 12.22,1.86C6.69,2.24 2.23,6.7 1.85,12.23C1.41,18.75 6.56,24.19 13.02,24.19"
|
||||
android:strokeWidth="2.18182"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#3D3D3D"/>
|
||||
</vector>
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
<resources>
|
||||
<string name="app_name">Omnivore</string>
|
||||
<string name="gcp_id">590528454690-mpmbvcb4c3ifmnojmrultt37eo8f64at.apps.googleusercontent.com</string>
|
||||
<string name="welcome_title">Everything you read.\nSafe, organized, and\neasy to share.</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Theme.Omnivore" parent="android:Theme.Material.Light">
|
||||
|
||||
<style name="Theme.Omnivore" parent="android:Theme.Material.Light.NoActionBar">
|
||||
</style>
|
||||
|
||||
<style name="Theme.AppCompat.Translucent" parent="Theme.AppCompat.NoActionBar">
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0xA8",
|
||||
"green" : "0xEB",
|
||||
"green" : "0xEA",
|
||||
"red" : "0xFB"
|
||||
}
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue