allow full window draw

This commit is contained in:
Stefano Sansone
2024-01-31 13:23:39 +00:00
parent 1fa061ee0a
commit d101952dee
6 changed files with 255 additions and 252 deletions

View File

@ -22,6 +22,7 @@
<activity
android:name=".MainActivity"
android:exported="true"
android:windowSoftInputMode="adjustResize"
android:theme="@style/Theme.Omnivore">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@ -4,12 +4,11 @@ import android.os.Bundle
import android.view.View
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
@ -24,61 +23,65 @@ import app.omnivore.omnivore.ui.settings.SettingsViewModel
import app.omnivore.omnivore.ui.theme.OmnivoreTheme
import com.pspdfkit.PSPDFKit
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@OptIn(DelicateCoroutinesApi::class)
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val loginViewModel: LoginViewModel by viewModels()
val libraryViewModel: LibraryViewModel by viewModels()
val settingsViewModel: SettingsViewModel by viewModels()
val searchViewModel: SearchViewModel by viewModels()
val labelsViewModel: LabelsViewModel by viewModels()
val saveViewModel: SaveViewModel by viewModels()
val editInfoViewModel: EditInfoViewModel by viewModels()
val loginViewModel: LoginViewModel by viewModels()
val libraryViewModel: LibraryViewModel by viewModels()
val settingsViewModel: SettingsViewModel by viewModels()
val searchViewModel: SearchViewModel by viewModels()
val labelsViewModel: LabelsViewModel by viewModels()
val saveViewModel: SaveViewModel by viewModels()
val editInfoViewModel: EditInfoViewModel by viewModels()
val context = this
val context = this
GlobalScope.launch(Dispatchers.IO) {
val licenseKey = getString(R.string.pspdfkit_license_key)
GlobalScope.launch(Dispatchers.IO) {
val licenseKey = getString(R.string.pspdfkit_license_key)
if (licenseKey.length > 30) {
PSPDFKit.initialize(context, licenseKey)
} else {
PSPDFKit.initialize(context, null)
}
}
setContent {
OmnivoreTheme {
Box(
modifier = Modifier
.fillMaxSize()
.background(color = Color.Black)
) {
RootView(
loginViewModel,
searchViewModel,
libraryViewModel,
settingsViewModel,
labelsViewModel,
saveViewModel,
editInfoViewModel)
if (licenseKey.length > 30) {
PSPDFKit.initialize(context, licenseKey)
} else {
PSPDFKit.initialize(context, null)
}
}
}
}
// animate the view up when keyboard appears
WindowCompat.setDecorFitsSystemWindows(window, false)
val rootView = findViewById<View>(android.R.id.content).rootView
ViewCompat.setOnApplyWindowInsetsListener(rootView) { _, insets ->
val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
rootView.setPadding(0, 0, 0, imeHeight)
insets
enableEdgeToEdge()
setContent {
OmnivoreTheme {
Box(
modifier = Modifier
.fillMaxSize()
) {
RootView(
loginViewModel,
searchViewModel,
libraryViewModel,
settingsViewModel,
labelsViewModel,
saveViewModel,
editInfoViewModel
)
}
}
}
// animate the view up when keyboard appears
WindowCompat.setDecorFitsSystemWindows(window, false)
val rootView = findViewById<View>(android.R.id.content).rootView
ViewCompat.setOnApplyWindowInsetsListener(rootView) { _, insets ->
val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
rootView.setPadding(0, 0, 0, imeHeight)
insets
}
}
}
}

View File

@ -4,7 +4,7 @@ import android.annotation.SuppressLint
import android.content.Intent
import android.net.Uri
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material3.*
@ -14,7 +14,6 @@ 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
@ -23,154 +22,173 @@ import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import app.omnivore.omnivore.R
import app.omnivore.omnivore.ui.theme.OmnivoreTheme
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.google.android.gms.common.GoogleApiAvailability
import kotlinx.coroutines.launch
@Composable
fun WelcomeScreen(viewModel: LoginViewModel) {
OmnivoreTheme(darkTheme = false) {
Surface(modifier = Modifier.fillMaxSize(), color = Color(0xFFFCEBA8)) {
WelcomeScreenContent(viewModel = viewModel)
val systemUiController = rememberSystemUiController()
val useDarkIcons = !isSystemInDarkTheme()
DisposableEffect(systemUiController, useDarkIcons) {
systemUiController.setSystemBarsColor(
color = Color.Black,
darkIcons = true
)
onDispose {
systemUiController.setSystemBarsColor(
color = Color.Black,
darkIcons = useDarkIcons
)
}
}
OmnivoreTheme(darkTheme = false) {
Surface(modifier = Modifier.fillMaxSize(), color = Color(0xFFFCEBA8)) {
WelcomeScreenContent(viewModel = viewModel)
}
}
}
}
@SuppressLint("CoroutineCreationDuringComposition")
@Composable
fun WelcomeScreenContent(viewModel: LoginViewModel) {
val registrationState: RegistrationState by viewModel
.registrationStateLiveData
.observeAsState(RegistrationState.SocialLogin)
val registrationState: RegistrationState by viewModel
.registrationStateLiveData
.observeAsState(RegistrationState.SocialLogin)
val snackBarHostState = remember { SnackbarHostState() }
val coroutineScope = rememberCoroutineScope()
val focusManager = LocalFocusManager.current
val snackBarHostState = remember { SnackbarHostState() }
val coroutineScope = rememberCoroutineScope()
Column(
verticalArrangement = Arrangement.SpaceAround,
horizontalAlignment = Alignment.Start,
modifier = Modifier
.fillMaxSize()
.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)
}
RegistrationState.EmailSignUp -> {
EmailSignUpView(viewModel = viewModel)
}
RegistrationState.SelfHosted -> {
SelfHostedView(viewModel = viewModel)
}
RegistrationState.SocialLogin -> {
Text(
text = stringResource(id = R.string.welcome_title),
style = MaterialTheme.typography.headlineLarge
Column(
verticalArrangement = Arrangement.SpaceAround,
horizontalAlignment = Alignment.Start,
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
Spacer(modifier = Modifier.height(50.dp))
Image(
painter = painterResource(id = R.drawable.ic_omnivore_name_logo),
contentDescription = "Omnivore Icon with Name"
)
Text(
text = stringResource(id = R.string.welcome_subtitle),
style = MaterialTheme.typography.titleSmall
)
MoreInfoButton()
Spacer(modifier = Modifier.height(50.dp))
AuthProviderView(viewModel = viewModel)
}
RegistrationState.PendingUser -> {
CreateUserProfileView(viewModel = viewModel)
}
when (registrationState) {
RegistrationState.EmailSignIn -> {
EmailLoginView(viewModel = viewModel)
}
RegistrationState.EmailSignUp -> {
EmailSignUpView(viewModel = viewModel)
}
RegistrationState.SelfHosted -> {
SelfHostedView(viewModel = viewModel)
}
RegistrationState.SocialLogin -> {
Text(
text = stringResource(id = R.string.welcome_title),
style = MaterialTheme.typography.headlineLarge
)
Text(
text = stringResource(id = R.string.welcome_subtitle),
style = MaterialTheme.typography.titleSmall
)
MoreInfoButton()
Spacer(modifier = Modifier.height(50.dp))
AuthProviderView(viewModel = viewModel)
}
RegistrationState.PendingUser -> {
CreateUserProfileView(viewModel = viewModel)
}
}
Spacer(modifier = Modifier.weight(1.0F))
}
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()
else -> {}
}
}
if (viewModel.errorMessage != null) {
coroutineScope.launch {
val result = snackBarHostState
.showSnackbar(
viewModel.errorMessage!!,
actionLabel = "Dismiss",
duration = SnackbarDuration.Indefinite
)
when (result) {
SnackbarResult.ActionPerformed -> viewModel.resetErrorMessage()
else -> {}
}
SnackbarHost(hostState = snackBarHostState)
}
SnackbarHost(hostState = snackBarHostState)
}
}
@Composable
fun AuthProviderView(viewModel: LoginViewModel) {
val isGoogleAuthAvailable: Boolean = GoogleApiAvailability
.getInstance()
.isGooglePlayServicesAvailable(LocalContext.current) == 0
val isGoogleAuthAvailable: Boolean = GoogleApiAvailability
.getInstance()
.isGooglePlayServicesAvailable(LocalContext.current) == 0
Row(
horizontalArrangement = Arrangement.Center
) {
Spacer(modifier = Modifier.weight(1.0F))
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalAlignment = Alignment.CenterHorizontally
Row(
horizontalArrangement = Arrangement.Center
) {
if (isGoogleAuthAvailable) {
GoogleAuthButton(viewModel)
}
Spacer(modifier = Modifier.weight(1.0F))
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
if (isGoogleAuthAvailable) {
GoogleAuthButton(viewModel)
}
AppleAuthButton(viewModel)
AppleAuthButton(viewModel)
ClickableText(
text = AnnotatedString(stringResource(R.string.welcome_screen_action_continue_with_email)),
style = MaterialTheme.typography.titleMedium
.plus(TextStyle(textDecoration = TextDecoration.Underline)),
onClick = { viewModel.showEmailSignIn() }
)
ClickableText(
text = AnnotatedString(stringResource(R.string.welcome_screen_action_continue_with_email)),
style = MaterialTheme.typography.titleMedium
.plus(TextStyle(textDecoration = TextDecoration.Underline)),
onClick = { viewModel.showEmailSignIn() }
)
Spacer(modifier = Modifier.weight(1.0F))
Spacer(modifier = Modifier.weight(1.0F))
ClickableText(
text = AnnotatedString(stringResource(R.string.welcome_screen_action_self_hosting_options)),
style = MaterialTheme.typography.titleMedium
.plus(TextStyle(textDecoration = TextDecoration.Underline)),
onClick = { viewModel.showSelfHostedSettings() },
modifier = Modifier
.padding(vertical = 10.dp)
)
ClickableText(
text = AnnotatedString(stringResource(R.string.welcome_screen_action_self_hosting_options)),
style = MaterialTheme.typography.titleMedium
.plus(TextStyle(textDecoration = TextDecoration.Underline)),
onClick = { viewModel.showSelfHostedSettings() },
modifier = Modifier
.padding(vertical = 10.dp)
)
}
Spacer(modifier = Modifier.weight(1.0F))
}
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")) }
val context = LocalContext.current
val intent = remember { Intent(Intent.ACTION_VIEW, Uri.parse("https://omnivore.app/about")) }
ClickableText(
text = AnnotatedString(
stringResource(id = R.string.learn_more),
),
style = MaterialTheme.typography.titleSmall
.plus(TextStyle(textDecoration = TextDecoration.Underline)),
onClick = {
context.startActivity(intent)
},
modifier = Modifier.padding(vertical = 6.dp)
)
ClickableText(
text = AnnotatedString(
stringResource(id = R.string.learn_more),
),
style = MaterialTheme.typography.titleSmall
.plus(TextStyle(textDecoration = TextDecoration.Underline)),
onClick = {
context.startActivity(intent)
},
modifier = Modifier.padding(vertical = 6.dp)
)
}

View File

@ -20,7 +20,6 @@ import androidx.compose.material.DismissValue
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.FractionalThreshold
import androidx.compose.material.Icon
import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.material.Scaffold
import androidx.compose.material.ScaffoldState
import androidx.compose.material.SwipeToDismiss
@ -32,11 +31,11 @@ import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material.rememberDismissState
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.material.rememberScaffoldState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
@ -70,7 +69,6 @@ import app.omnivore.omnivore.ui.savedItemViews.SavedItemCard
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun LibraryView(
libraryViewModel: LibraryViewModel,
@ -146,7 +144,7 @@ fun showAddLinkBottomSheet(libraryViewModel: LibraryViewModel) {
libraryViewModel.bottomSheetState.value = LibraryBottomSheetState.ADD_LINK
}
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LabelBottomSheet(
libraryViewModel: LibraryViewModel,

View File

@ -1,14 +1,10 @@
package app.omnivore.omnivore.ui.root
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
@ -18,109 +14,98 @@ import app.omnivore.omnivore.ui.auth.WelcomeScreen
import app.omnivore.omnivore.ui.components.LabelsViewModel
import app.omnivore.omnivore.ui.editinfo.EditInfoViewModel
import app.omnivore.omnivore.ui.library.LibraryView
import app.omnivore.omnivore.ui.library.SearchView
import app.omnivore.omnivore.ui.library.LibraryViewModel
import app.omnivore.omnivore.ui.library.SearchView
import app.omnivore.omnivore.ui.library.SearchViewModel
import app.omnivore.omnivore.ui.save.SaveViewModel
import app.omnivore.omnivore.ui.settings.PolicyWebView
import app.omnivore.omnivore.ui.settings.SettingsView
import app.omnivore.omnivore.ui.settings.SettingsViewModel
import com.google.accompanist.systemuicontroller.rememberSystemUiController
@Composable
fun RootView(
loginViewModel: LoginViewModel,
searchViewModel: SearchViewModel,
libraryViewModel: LibraryViewModel,
settingsViewModel: SettingsViewModel,
labelsViewModel: LabelsViewModel,
saveViewModel: SaveViewModel,
editInfoViewModel: EditInfoViewModel,
loginViewModel: LoginViewModel,
searchViewModel: SearchViewModel,
libraryViewModel: LibraryViewModel,
settingsViewModel: SettingsViewModel,
labelsViewModel: LabelsViewModel,
saveViewModel: SaveViewModel,
editInfoViewModel: EditInfoViewModel,
) {
val hasAuthToken: Boolean by loginViewModel.hasAuthTokenLiveData.observeAsState(false)
val systemUiController = rememberSystemUiController()
val useDarkIcons = !isSystemInDarkTheme()
val hasAuthToken: Boolean by loginViewModel.hasAuthTokenLiveData.observeAsState(false)
DisposableEffect(systemUiController, useDarkIcons) {
systemUiController.setSystemBarsColor(
color = Color.Black,
darkIcons = false
)
Box {
if (hasAuthToken) {
PrimaryNavigator(
loginViewModel = loginViewModel,
searchViewModel = searchViewModel,
libraryViewModel = libraryViewModel,
settingsViewModel = settingsViewModel,
labelsViewModel = labelsViewModel,
saveViewModel = saveViewModel,
editInfoViewModel = editInfoViewModel,
)
} else {
WelcomeScreen(viewModel = loginViewModel)
}
onDispose {}
}
Box(
modifier = Modifier
.systemBarsPadding()
) {
if (hasAuthToken) {
PrimaryNavigator(
loginViewModel = loginViewModel,
searchViewModel = searchViewModel,
libraryViewModel = libraryViewModel,
settingsViewModel = settingsViewModel,
labelsViewModel = labelsViewModel,
saveViewModel = saveViewModel,
editInfoViewModel = editInfoViewModel,
)
} else {
WelcomeScreen(viewModel = loginViewModel)
DisposableEffect(hasAuthToken) {
if (hasAuthToken) {
loginViewModel.registerUser()
}
onDispose {}
}
}
DisposableEffect(hasAuthToken) {
if (hasAuthToken) {
loginViewModel.registerUser()
}
onDispose {}
}
}
}
@Composable
fun PrimaryNavigator(
loginViewModel: LoginViewModel,
libraryViewModel: LibraryViewModel,
searchViewModel: SearchViewModel,
settingsViewModel: SettingsViewModel,
labelsViewModel: LabelsViewModel,
saveViewModel: SaveViewModel,
editInfoViewModel: EditInfoViewModel,
loginViewModel: LoginViewModel,
libraryViewModel: LibraryViewModel,
searchViewModel: SearchViewModel,
settingsViewModel: SettingsViewModel,
labelsViewModel: LabelsViewModel,
saveViewModel: SaveViewModel,
editInfoViewModel: EditInfoViewModel,
) {
val navController = rememberNavController()
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Routes.Library.route) {
composable(Routes.Library.route) {
LibraryView(
libraryViewModel = libraryViewModel,
navController = navController,
labelsViewModel = labelsViewModel,
saveViewModel = saveViewModel,
editInfoViewModel = editInfoViewModel,
)
}
NavHost(navController = navController, startDestination = Routes.Library.route) {
composable(Routes.Library.route) {
LibraryView(
libraryViewModel = libraryViewModel,
navController = navController,
labelsViewModel = labelsViewModel,
saveViewModel = saveViewModel,
editInfoViewModel = editInfoViewModel,
)
}
composable(Routes.Search.route) {
SearchView(
viewModel = searchViewModel,
navController = navController
)
}
composable(Routes.Search.route) {
SearchView(
viewModel = searchViewModel,
navController = navController
)
}
composable(Routes.Settings.route) {
SettingsView(loginViewModel = loginViewModel, settingsViewModel = settingsViewModel, navController = navController)
}
composable(Routes.Settings.route) {
SettingsView(
loginViewModel = loginViewModel,
settingsViewModel = settingsViewModel,
navController = navController
)
}
composable(Routes.Documentation.route) {
PolicyWebView(navController = navController, url = "https://docs.omnivore.app")
}
composable(Routes.Documentation.route) {
PolicyWebView(navController = navController, url = "https://docs.omnivore.app")
}
composable(Routes.PrivacyPolicy.route) {
PolicyWebView(navController = navController, url = "https://omnivore.app/privacy")
}
composable(Routes.PrivacyPolicy.route) {
PolicyWebView(navController = navController, url = "https://omnivore.app/privacy")
}
composable(Routes.TermsAndConditions.route) {
PolicyWebView(navController = navController, url = "https://omnivore.app/app/terms")
composable(Routes.TermsAndConditions.route) {
PolicyWebView(navController = navController, url = "https://omnivore.app/app/terms")
}
}
}
}

View File

@ -18,7 +18,5 @@
<item name="android:windowFullscreen">true</item>
<item name="android:windowIsFloating">false</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>