mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Merge pull request #3570 from stefanosansone/refactor/android-settings
Android - Settings screen refactor
This commit is contained in:
commit
f318923eed
20 changed files with 727 additions and 406 deletions
|
|
@ -35,7 +35,6 @@ class MainActivity : ComponentActivity() {
|
|||
super.onCreate(savedInstanceState)
|
||||
|
||||
val loginViewModel: LoginViewModel by viewModels()
|
||||
val settingsViewModel: SettingsViewModel by viewModels()
|
||||
val searchViewModel: SearchViewModel by viewModels()
|
||||
val labelsViewModel: LabelsViewModel by viewModels()
|
||||
val saveViewModel: SaveViewModel by viewModels()
|
||||
|
|
@ -64,7 +63,6 @@ class MainActivity : ComponentActivity() {
|
|||
RootView(
|
||||
loginViewModel,
|
||||
searchViewModel,
|
||||
settingsViewModel,
|
||||
labelsViewModel,
|
||||
saveViewModel,
|
||||
editInfoViewModel
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
package app.omnivore.omnivore.core.ui
|
||||
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun LinkIcon(
|
||||
label: String,
|
||||
icon: ImageVector,
|
||||
url: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
IconButton(
|
||||
modifier = modifier.padding(4.dp),
|
||||
onClick = { uriHandler.openUri(url) },
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = label,
|
||||
modifier = Modifier.size(24.dp),
|
||||
tint = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -411,7 +411,7 @@ fun LibraryViewContent(
|
|||
dismissContent = {
|
||||
val selected =
|
||||
currentItem.savedItemId == selectedItem?.savedItem?.savedItemId
|
||||
val test = SavedItemWithLabelsAndHighlights(
|
||||
val savedItem = SavedItemWithLabelsAndHighlights(
|
||||
savedItem = cardDataWithLabels.savedItem,
|
||||
labels = cardDataWithLabels.labels,
|
||||
highlights = cardDataWithLabels.highlights
|
||||
|
|
@ -419,7 +419,7 @@ fun LibraryViewContent(
|
|||
SavedItemCard(
|
||||
selected = selected,
|
||||
savedItemViewModel = libraryViewModel,
|
||||
savedItem = test,
|
||||
savedItem = savedItem,
|
||||
onClickHandler = {
|
||||
libraryViewModel.actionsMenuItemLiveData.postValue(null)
|
||||
val activityClass =
|
||||
|
|
|
|||
|
|
@ -1,10 +1,23 @@
|
|||
package app.omnivore.omnivore.feature.root
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
|
|
@ -16,41 +29,57 @@ import app.omnivore.omnivore.feature.library.LibraryView
|
|||
import app.omnivore.omnivore.feature.library.SearchView
|
||||
import app.omnivore.omnivore.feature.library.SearchViewModel
|
||||
import app.omnivore.omnivore.feature.save.SaveViewModel
|
||||
import app.omnivore.omnivore.feature.settings.PolicyWebView
|
||||
import app.omnivore.omnivore.feature.settings.SettingsView
|
||||
import app.omnivore.omnivore.feature.settings.SettingsViewModel
|
||||
import app.omnivore.omnivore.feature.settings.SettingsScreen
|
||||
import app.omnivore.omnivore.feature.settings.about.AboutScreen
|
||||
import app.omnivore.omnivore.feature.settings.account.AccountScreen
|
||||
import app.omnivore.omnivore.feature.web.WebViewScreen
|
||||
import app.omnivore.omnivore.navigation.Routes
|
||||
|
||||
@Composable
|
||||
fun RootView(
|
||||
loginViewModel: LoginViewModel,
|
||||
searchViewModel: SearchViewModel,
|
||||
settingsViewModel: SettingsViewModel,
|
||||
labelsViewModel: LabelsViewModel,
|
||||
saveViewModel: SaveViewModel,
|
||||
editInfoViewModel: EditInfoViewModel,
|
||||
) {
|
||||
val hasAuthToken: Boolean by loginViewModel.hasAuthTokenLiveData.observeAsState(false)
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
|
||||
Box {
|
||||
if (hasAuthToken) {
|
||||
PrimaryNavigator(
|
||||
loginViewModel = loginViewModel,
|
||||
searchViewModel = searchViewModel,
|
||||
settingsViewModel = settingsViewModel,
|
||||
labelsViewModel = labelsViewModel,
|
||||
saveViewModel = saveViewModel,
|
||||
editInfoViewModel = editInfoViewModel,
|
||||
)
|
||||
} else {
|
||||
WelcomeScreen(viewModel = loginViewModel)
|
||||
}
|
||||
|
||||
DisposableEffect(hasAuthToken) {
|
||||
Scaffold(
|
||||
snackbarHost = { SnackbarHost(snackbarHostState) },
|
||||
) { padding ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(padding)
|
||||
.consumeWindowInsets(padding)
|
||||
.windowInsetsPadding(
|
||||
WindowInsets.safeDrawing.only(
|
||||
WindowInsetsSides.Horizontal,
|
||||
),
|
||||
)
|
||||
){
|
||||
if (hasAuthToken) {
|
||||
loginViewModel.registerUser()
|
||||
PrimaryNavigator(
|
||||
loginViewModel = loginViewModel,
|
||||
searchViewModel = searchViewModel,
|
||||
labelsViewModel = labelsViewModel,
|
||||
saveViewModel = saveViewModel,
|
||||
editInfoViewModel = editInfoViewModel,
|
||||
snackbarHostState = snackbarHostState
|
||||
|
||||
)
|
||||
} else {
|
||||
WelcomeScreen(viewModel = loginViewModel)
|
||||
}
|
||||
|
||||
DisposableEffect(hasAuthToken) {
|
||||
if (hasAuthToken) {
|
||||
loginViewModel.registerUser()
|
||||
}
|
||||
onDispose {}
|
||||
}
|
||||
onDispose {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -59,14 +88,17 @@ fun RootView(
|
|||
fun PrimaryNavigator(
|
||||
loginViewModel: LoginViewModel,
|
||||
searchViewModel: SearchViewModel,
|
||||
settingsViewModel: SettingsViewModel,
|
||||
labelsViewModel: LabelsViewModel,
|
||||
saveViewModel: SaveViewModel,
|
||||
editInfoViewModel: EditInfoViewModel,
|
||||
snackbarHostState: SnackbarHostState
|
||||
) {
|
||||
val navController = rememberNavController()
|
||||
|
||||
NavHost(navController = navController, startDestination = Routes.Library.route) {
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = Routes.Library.route
|
||||
) {
|
||||
composable(Routes.Library.route) {
|
||||
LibraryView(
|
||||
navController = navController,
|
||||
|
|
@ -78,29 +110,40 @@ fun PrimaryNavigator(
|
|||
|
||||
composable(Routes.Search.route) {
|
||||
SearchView(
|
||||
viewModel = searchViewModel,
|
||||
navController = navController
|
||||
viewModel = searchViewModel, navController = navController
|
||||
)
|
||||
}
|
||||
|
||||
composable(Routes.Settings.route) {
|
||||
SettingsView(
|
||||
SettingsScreen(
|
||||
loginViewModel = loginViewModel,
|
||||
settingsViewModel = settingsViewModel,
|
||||
navController = navController
|
||||
)
|
||||
}
|
||||
|
||||
composable(Routes.About.route) {
|
||||
AboutScreen(
|
||||
navController = navController
|
||||
)
|
||||
}
|
||||
|
||||
composable(Routes.Account.route) {
|
||||
AccountScreen(
|
||||
navController = navController,
|
||||
snackbarHostState = snackbarHostState,
|
||||
)
|
||||
}
|
||||
|
||||
composable(Routes.Documentation.route) {
|
||||
PolicyWebView(navController = navController, url = "https://docs.omnivore.app")
|
||||
WebViewScreen(navController = navController, url = "https://docs.omnivore.app")
|
||||
}
|
||||
|
||||
composable(Routes.PrivacyPolicy.route) {
|
||||
PolicyWebView(navController = navController, url = "https://omnivore.app/privacy")
|
||||
WebViewScreen(navController = navController, url = "https://omnivore.app/privacy")
|
||||
}
|
||||
|
||||
composable(Routes.TermsAndConditions.route) {
|
||||
PolicyWebView(navController = navController, url = "https://omnivore.app/app/terms")
|
||||
WebViewScreen(navController = navController, url = "https://omnivore.app/app/terms")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package app.omnivore.omnivore.feature.settings
|
||||
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
|
@ -12,31 +14,37 @@ import com.google.android.gms.auth.api.signin.GoogleSignInOptions
|
|||
|
||||
@Composable
|
||||
fun LogoutDialog(onClose: (Boolean) -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val context = LocalContext.current
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = { onClose(false) },
|
||||
title = { Text(text = stringResource(R.string.logout_dialog_title)) },
|
||||
text = {
|
||||
Text(stringResource(R.string.logout_dialog_confirm_msg))
|
||||
},
|
||||
confirmButton = {
|
||||
Button(onClick = {
|
||||
// Sign out google users
|
||||
val signInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
|
||||
.build()
|
||||
AlertDialog(onDismissRequest = { onClose(false) },
|
||||
title = { Text(text = stringResource(R.string.logout_dialog_title)) },
|
||||
text = {
|
||||
Text(stringResource(R.string.logout_dialog_confirm_msg))
|
||||
},
|
||||
containerColor = MaterialTheme.colorScheme.background,
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
contentColor = MaterialTheme.colorScheme.onSurface
|
||||
),
|
||||
onClick = {
|
||||
val signInOptions =
|
||||
GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN).build()
|
||||
|
||||
val googleSignIn = GoogleSignIn.getClient(context, signInOptions)
|
||||
googleSignIn.signOut()
|
||||
onClose(true)
|
||||
}) {
|
||||
Text(stringResource(R.string.logout_dialog_action_confirm))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
Button(onClick = { onClose(false) }) {
|
||||
Text(stringResource(R.string.logout_dialog_action_cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
val googleSignIn = GoogleSignIn.getClient(context, signInOptions)
|
||||
googleSignIn.signOut()
|
||||
onClose(true)
|
||||
}) {
|
||||
Text(stringResource(R.string.logout_dialog_action_confirm))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
colors = ButtonDefaults.textButtonColors(
|
||||
contentColor = MaterialTheme.colorScheme.onSurface
|
||||
),
|
||||
onClick = { onClose(false) }) {
|
||||
Text(stringResource(R.string.logout_dialog_action_cancel))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,73 +0,0 @@
|
|||
package app.omnivore.omnivore.feature.settings
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import app.omnivore.omnivore.R
|
||||
|
||||
@Composable
|
||||
fun ManageAccountDialog(onDismiss: () -> Unit, settingsViewModel: SettingsViewModel) {
|
||||
Dialog(onDismissRequest = { onDismiss() }) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
color = Color.White,
|
||||
modifier = Modifier
|
||||
.height(300.dp)
|
||||
) {
|
||||
ManageAccountView(settingsViewModel = settingsViewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ManageAccountView(settingsViewModel: SettingsViewModel) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(top = 6.dp, start = 6.dp, end = 6.dp, bottom = 6.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 12.dp, bottom = 12.dp),
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
Text(stringResource(R.string.manage_account_title))
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.clickable(onClick = { settingsViewModel.resetDataCache() })
|
||||
) {
|
||||
Text(stringResource(R.string.manage_account_action_reset_data_cache))
|
||||
Spacer(modifier = Modifier.weight(1.0F))
|
||||
Icon(imageVector = Icons.Filled.Refresh, contentDescription = null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
package app.omnivore.omnivore.feature.settings
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.navigation.NavHostController
|
||||
import app.omnivore.omnivore.navigation.Routes
|
||||
import app.omnivore.omnivore.R
|
||||
|
||||
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter", "SetJavaScriptEnabled")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun PolicyWebView(navController: NavHostController, url: String) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.policy_webview_title)) },
|
||||
actions = {
|
||||
IconButton(onClick = { navController.navigate(Routes.Settings.route) }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Settings,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}, colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant
|
||||
)
|
||||
)
|
||||
}
|
||||
) {
|
||||
val isDarkMode = isSystemInDarkTheme()
|
||||
|
||||
Box {
|
||||
AndroidView(factory = {
|
||||
WebView(it).apply {
|
||||
if (isDarkMode) {
|
||||
setBackgroundColor(Color.Transparent.hashCode())
|
||||
}
|
||||
|
||||
layoutParams = ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
|
||||
settings.javaScriptEnabled = true
|
||||
settings.allowContentAccess = true
|
||||
settings.allowFileAccess = true
|
||||
settings.domStorageEnabled = true
|
||||
|
||||
alpha = 0.0f
|
||||
|
||||
webViewClient = object : WebViewClient() {
|
||||
override fun onPageFinished(view: WebView?, url: String?) {
|
||||
super.onPageFinished(view, url)
|
||||
view?.animate()?.alpha(1.0f)?.duration = 200
|
||||
}
|
||||
}
|
||||
|
||||
// val themeID = if (isDarkMode) "Gray" else "LightGray"
|
||||
// loadUrl(url, mutableMapOf("Set-Cookie" to "theme=$themeID; Max-Age=31536000;"))
|
||||
loadUrl(url, mutableMapOf())
|
||||
}
|
||||
}, update = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,175 +0,0 @@
|
|||
package app.omnivore.omnivore.feature.settings
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavHostController
|
||||
import app.omnivore.omnivore.BuildConfig
|
||||
import app.omnivore.omnivore.R
|
||||
import app.omnivore.omnivore.navigation.Routes
|
||||
import app.omnivore.omnivore.feature.auth.LoginViewModel
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SettingsView(
|
||||
loginViewModel: LoginViewModel,
|
||||
settingsViewModel: SettingsViewModel,
|
||||
navController: NavHostController,
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.settings_view_title)) },
|
||||
actions = {
|
||||
IconButton(onClick = { navController.navigate(Routes.Library.route) }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Home,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}, colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant
|
||||
)
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
SettingsViewContent(
|
||||
loginViewModel = loginViewModel,
|
||||
settingsViewModel = settingsViewModel,
|
||||
navController = navController,
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
top = paddingValues.calculateTopPadding(),
|
||||
bottom = paddingValues.calculateBottomPadding()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SettingsViewContent(loginViewModel: LoginViewModel, settingsViewModel: SettingsViewModel, navController: NavHostController, modifier: Modifier) {
|
||||
val showLogoutDialog = remember { mutableStateOf(false) }
|
||||
val showManageAccountDialog = remember { mutableStateOf(false ) }
|
||||
|
||||
Box(
|
||||
modifier = modifier.fillMaxSize()
|
||||
) {
|
||||
|
||||
val version = "Omnivore Version: " + BuildConfig.VERSION_NAME
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Top,
|
||||
horizontalAlignment = Alignment.Start,
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 6.dp)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
|
||||
|
||||
SettingRow(text = stringResource(R.string.settings_view_setting_row_documentation)) {
|
||||
navController.navigate(Routes.Documentation.route)
|
||||
}
|
||||
RowDivider()
|
||||
SettingRow(text = stringResource(R.string.settings_view_setting_row_feedback)) {
|
||||
settingsViewModel.presentIntercom()
|
||||
}
|
||||
RowDivider()
|
||||
SettingRow(text = stringResource(R.string.settings_view_setting_row_privacy_policy)) {
|
||||
navController.navigate(Routes.PrivacyPolicy.route)
|
||||
}
|
||||
RowDivider()
|
||||
SettingRow(text = stringResource(R.string.settings_view_setting_row_terms_and_conditions)) {
|
||||
navController.navigate(Routes.TermsAndConditions.route)
|
||||
}
|
||||
|
||||
SectionSpacer()
|
||||
|
||||
SettingRow(text = stringResource(R.string.settings_view_setting_row_manage_account)) {
|
||||
showManageAccountDialog.value = true
|
||||
}
|
||||
RowDivider()
|
||||
SettingRow(text = stringResource(R.string.settings_view_setting_row_logout), includeIcon = false) {
|
||||
showLogoutDialog.value = true
|
||||
}
|
||||
RowDivider()
|
||||
|
||||
Text(
|
||||
text = version,
|
||||
fontSize = 12.sp,
|
||||
modifier = Modifier
|
||||
.padding(15.dp)
|
||||
)
|
||||
}
|
||||
|
||||
if (showLogoutDialog.value) {
|
||||
LogoutDialog { performLogout ->
|
||||
if (performLogout) {
|
||||
loginViewModel.logout()
|
||||
}
|
||||
showLogoutDialog.value = false
|
||||
}
|
||||
}
|
||||
|
||||
if (showManageAccountDialog.value) {
|
||||
ManageAccountDialog(
|
||||
onDismiss = { showManageAccountDialog.value = false },
|
||||
settingsViewModel = settingsViewModel
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RowDivider() {
|
||||
Divider(
|
||||
modifier = Modifier.padding(horizontal = 12.dp),
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SectionSpacer() {
|
||||
RowDivider()
|
||||
Spacer(Modifier.height(60.dp))
|
||||
RowDivider()
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingRow(text: String, includeIcon: Boolean = true, tapAction: () -> Unit) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { tapAction() }
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterVertically)
|
||||
.padding(16.dp),
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
|
||||
if (includeIcon) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.chevron_right),
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
package app.omnivore.omnivore.feature.settings
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavHostController
|
||||
import app.omnivore.omnivore.R
|
||||
import app.omnivore.omnivore.feature.auth.LoginViewModel
|
||||
import app.omnivore.omnivore.navigation.Routes
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
internal fun SettingsScreen(
|
||||
loginViewModel: LoginViewModel,
|
||||
navController: NavHostController
|
||||
) {
|
||||
Scaffold(topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.settings_view_title)) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { navController.navigateUp() }) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Outlined.ArrowBack,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.background
|
||||
),
|
||||
)
|
||||
}) { paddingValues ->
|
||||
SettingsViewContent(
|
||||
loginViewModel = loginViewModel,
|
||||
navController = navController,
|
||||
paddingValues = paddingValues
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SettingsViewContent(
|
||||
loginViewModel: LoginViewModel,
|
||||
navController: NavHostController,
|
||||
paddingValues: PaddingValues
|
||||
) {
|
||||
val showLogoutDialog = remember { mutableStateOf(false) }
|
||||
|
||||
val state = rememberLazyListState()
|
||||
|
||||
LazyColumn(
|
||||
state = state,
|
||||
contentPadding = paddingValues,
|
||||
) {
|
||||
|
||||
item {
|
||||
SettingRow(title = stringResource(R.string.settings_view_setting_row_manage_account)) {
|
||||
navController.navigate(Routes.Account.route)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
SettingRow(
|
||||
title = stringResource(R.string.settings_view_setting_row_logout)
|
||||
) {
|
||||
showLogoutDialog.value = true
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
SettingRow(title = stringResource(R.string.about_view_title)) {
|
||||
navController.navigate(Routes.About.route)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showLogoutDialog.value) {
|
||||
LogoutDialog { performLogout ->
|
||||
if (performLogout) {
|
||||
loginViewModel.logout()
|
||||
}
|
||||
showLogoutDialog.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
internal fun SettingRow(
|
||||
title: String, subtitle: String? = null, onClick: (() -> Unit)?
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable(enabled = onClick != null, onClick = { onClick?.invoke() })
|
||||
.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(vertical = SettingsVerticalPadding)
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(horizontal = SettingsHorizontalPadding),
|
||||
text = title,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 2,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontSize = SettingsTitleFontSize,
|
||||
)
|
||||
if (!subtitle.isNullOrBlank()) {
|
||||
Text(
|
||||
text = subtitle,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = SettingsHorizontalPadding)
|
||||
.alpha(SettingsSecondaryItemAlpha),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
maxLines = 10,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal val SettingsHorizontalPadding = 16.dp
|
||||
internal val SettingsVerticalPadding = 16.dp
|
||||
internal const val SettingsSecondaryItemAlpha = .78f
|
||||
internal val SettingsTitleFontSize = 16.sp
|
||||
|
||||
internal const val RELEASE_URL = "https://github.com/omnivore-app/omnivore/releases"
|
||||
|
|
@ -1,40 +1,58 @@
|
|||
package app.omnivore.omnivore.feature.settings
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import app.omnivore.omnivore.utils.DatastoreKeys
|
||||
import app.omnivore.omnivore.core.datastore.DatastoreRepository
|
||||
import app.omnivore.omnivore.core.data.DataService
|
||||
import app.omnivore.omnivore.core.datastore.DatastoreRepository
|
||||
import app.omnivore.omnivore.core.network.Networker
|
||||
import app.omnivore.omnivore.core.network.viewer
|
||||
import app.omnivore.omnivore.utils.DatastoreKeys
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import io.intercom.android.sdk.Intercom
|
||||
import io.intercom.android.sdk.IntercomSpace
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class SettingsViewModel @Inject constructor(
|
||||
private val networker: Networker,
|
||||
private val dataService: DataService,
|
||||
private val datastoreRepo: DatastoreRepository
|
||||
): ViewModel() {
|
||||
fun resetDataCache() {
|
||||
viewModelScope.launch {
|
||||
datastoreRepo.clearValue(DatastoreKeys.libraryLastSyncTimestamp)
|
||||
dataService.clearDatabase()
|
||||
}
|
||||
}
|
||||
private val networker: Networker,
|
||||
private val dataService: DataService,
|
||||
private val datastoreRepo: DatastoreRepository
|
||||
) : ViewModel() {
|
||||
|
||||
fun presentIntercom() {
|
||||
viewModelScope.launch {
|
||||
val viewer = networker.viewer()
|
||||
viewer?.let { v ->
|
||||
v.intercomHash?.let { intercomHash ->
|
||||
Intercom.client().setUserHash(intercomHash)
|
||||
var snackbarMessage by mutableStateOf("Resetting data...")
|
||||
|
||||
var isResettingData by mutableStateOf(false)
|
||||
var isDataResetCompleted by mutableStateOf(false)
|
||||
|
||||
fun resetDataCache() {
|
||||
isResettingData = true
|
||||
|
||||
viewModelScope.launch {
|
||||
datastoreRepo.clearValue(DatastoreKeys.libraryLastSyncTimestamp)
|
||||
dataService.clearDatabase()
|
||||
delay(1000)
|
||||
isResettingData = false
|
||||
|
||||
if (!isDataResetCompleted) {
|
||||
isDataResetCompleted = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun presentIntercom() {
|
||||
viewModelScope.launch {
|
||||
val viewer = networker.viewer()
|
||||
viewer?.let { v ->
|
||||
v.intercomHash?.let { intercomHash ->
|
||||
Intercom.client().setUserHash(intercomHash)
|
||||
}
|
||||
Intercom.client().present(space = IntercomSpace.Messages)
|
||||
}
|
||||
}
|
||||
Intercom.client().present(space = IntercomSpace.Messages)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,152 @@
|
|||
package app.omnivore.omnivore.feature.settings.about
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
|
||||
import androidx.compose.material.icons.outlined.Public
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.navigation.NavHostController
|
||||
import app.omnivore.omnivore.BuildConfig
|
||||
import app.omnivore.omnivore.R
|
||||
import app.omnivore.omnivore.core.ui.LinkIcon
|
||||
import app.omnivore.omnivore.feature.settings.RELEASE_URL
|
||||
import app.omnivore.omnivore.feature.settings.SettingRow
|
||||
import app.omnivore.omnivore.feature.settings.SettingsViewModel
|
||||
import app.omnivore.omnivore.navigation.Routes
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
internal fun AboutScreen(
|
||||
navController: NavHostController,
|
||||
settingsViewModel: SettingsViewModel = hiltViewModel()
|
||||
) {
|
||||
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.about_view_title)) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { navController.navigateUp() }) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Outlined.ArrowBack, contentDescription = null
|
||||
)
|
||||
}
|
||||
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.background
|
||||
),
|
||||
)
|
||||
},
|
||||
) { contentPadding ->
|
||||
LazyColumn(
|
||||
contentPadding = contentPadding,
|
||||
) {
|
||||
item {
|
||||
LogoHeader()
|
||||
}
|
||||
|
||||
item {
|
||||
SettingRow(
|
||||
title = stringResource(R.string.about_view_row_whats_new),
|
||||
onClick = { uriHandler.openUri(RELEASE_URL) },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
SettingRow(title = stringResource(R.string.settings_view_setting_row_documentation)) {
|
||||
navController.navigate(Routes.Documentation.route)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
SettingRow(title = stringResource(R.string.settings_view_setting_row_feedback)) {
|
||||
settingsViewModel.presentIntercom()
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
SettingRow(title = stringResource(R.string.settings_view_setting_row_privacy_policy)) {
|
||||
navController.navigate(Routes.PrivacyPolicy.route)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
SettingRow(title = stringResource(R.string.settings_view_setting_row_terms_and_conditions)) {
|
||||
navController.navigate(Routes.TermsAndConditions.route)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
SettingRow(
|
||||
title = stringResource(R.string.about_view_row_version),
|
||||
subtitle = getVersionName(),
|
||||
onClick = { },
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
LinkIcon(
|
||||
label = stringResource(R.string.website),
|
||||
icon = Icons.Outlined.Public,
|
||||
url = "https://omnivore.app",
|
||||
)
|
||||
LinkIcon(
|
||||
label = "Discord",
|
||||
icon = ImageVector.vectorResource(R.drawable.ic_discord),
|
||||
url = "https://discord.gg/h2z5rppzz9",
|
||||
)
|
||||
LinkIcon(
|
||||
label = "X",
|
||||
icon = ImageVector.vectorResource(R.drawable.ic_x),
|
||||
url = "https://x.com/omnivoreapp",
|
||||
)
|
||||
|
||||
LinkIcon(
|
||||
label = "GitHub",
|
||||
icon = ImageVector.vectorResource(R.drawable.ic_github),
|
||||
url = "https://github.com/omnivore-app",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getVersionName(): String {
|
||||
return when {
|
||||
BuildConfig.DEBUG -> {
|
||||
"Debug ${BuildConfig.VERSION_NAME}"
|
||||
}
|
||||
else -> {
|
||||
"Stable ${BuildConfig.VERSION_NAME}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package app.omnivore.omnivore.feature.settings.about
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.omnivore.omnivore.R
|
||||
|
||||
@Composable
|
||||
fun LogoHeader() {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_omnivore),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
modifier = Modifier
|
||||
.padding(vertical = 32.dp)
|
||||
.size(128.dp),
|
||||
)
|
||||
|
||||
HorizontalDivider()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package app.omnivore.omnivore.feature.settings.account
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarDuration
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.SnackbarResult
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.navigation.NavHostController
|
||||
import app.omnivore.omnivore.R
|
||||
import app.omnivore.omnivore.feature.settings.SettingRow
|
||||
import app.omnivore.omnivore.feature.settings.SettingsViewModel
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
internal fun AccountScreen(
|
||||
navController: NavHostController,
|
||||
snackbarHostState: SnackbarHostState,
|
||||
settingsViewModel: SettingsViewModel = hiltViewModel()
|
||||
) {
|
||||
LaunchedEffect(settingsViewModel.isResettingData) {
|
||||
if (settingsViewModel.isResettingData) {
|
||||
val result = snackbarHostState.showSnackbar(
|
||||
message = settingsViewModel.snackbarMessage,
|
||||
duration = SnackbarDuration.Indefinite,
|
||||
)
|
||||
when (result) {
|
||||
SnackbarResult.Dismissed -> {
|
||||
settingsViewModel.isResettingData = false
|
||||
}
|
||||
SnackbarResult.ActionPerformed -> { }
|
||||
}
|
||||
} else if (settingsViewModel.isDataResetCompleted) {
|
||||
snackbarHostState.showSnackbar(
|
||||
message = "Data reset completed.",
|
||||
duration = SnackbarDuration.Short
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.manage_account_title)) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { navController.navigateUp() }) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Outlined.ArrowBack, contentDescription = null
|
||||
)
|
||||
}
|
||||
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.background
|
||||
),
|
||||
)
|
||||
},
|
||||
) { contentPadding ->
|
||||
LazyColumn(
|
||||
contentPadding = contentPadding,
|
||||
) {
|
||||
item {
|
||||
SettingRow(
|
||||
title = stringResource(R.string.manage_account_action_reset_data_cache),
|
||||
onClick = {
|
||||
settingsViewModel.resetDataCache()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package app.omnivore.omnivore.feature.web
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.navigation.NavHostController
|
||||
import app.omnivore.omnivore.R
|
||||
import app.omnivore.omnivore.navigation.Routes
|
||||
|
||||
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter", "SetJavaScriptEnabled")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun WebViewScreen(navController: NavHostController, url: String) {
|
||||
Scaffold(topBar = {
|
||||
TopAppBar(title = { Text(stringResource(R.string.policy_webview_title)) }, actions = {
|
||||
IconButton(onClick = { navController.navigate(Routes.Settings.route) }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Settings, contentDescription = null
|
||||
)
|
||||
}
|
||||
}, colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant
|
||||
)
|
||||
)
|
||||
}) {
|
||||
val isDarkMode = isSystemInDarkTheme()
|
||||
|
||||
Box {
|
||||
AndroidView(factory = {
|
||||
WebView(it).apply {
|
||||
if (isDarkMode) {
|
||||
setBackgroundColor(Color.Transparent.hashCode())
|
||||
}
|
||||
|
||||
layoutParams = ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
|
||||
settings.javaScriptEnabled = true
|
||||
settings.allowContentAccess = true
|
||||
settings.allowFileAccess = true
|
||||
settings.domStorageEnabled = true
|
||||
|
||||
alpha = 0.0f
|
||||
|
||||
webViewClient = object : WebViewClient() {
|
||||
override fun onPageFinished(view: WebView?, url: String?) {
|
||||
super.onPageFinished(view, url)
|
||||
view?.animate()?.alpha(1.0f)?.duration = 200
|
||||
}
|
||||
}
|
||||
loadUrl(url, mutableMapOf())
|
||||
}
|
||||
}, update = {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@ package app.omnivore.omnivore.navigation
|
|||
sealed class Routes(val route: String) {
|
||||
object Library : Routes("Library")
|
||||
object Settings : Routes("Settings")
|
||||
object About : Routes("About")
|
||||
object Account : Routes("Account")
|
||||
object Search : Routes("Search")
|
||||
object Documentation : Routes("Documentation")
|
||||
object PrivacyPolicy : Routes("PrivacyPolicy")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="127.14dp"
|
||||
android:height="96.36dp"
|
||||
android:viewportWidth="127.14"
|
||||
android:viewportHeight="96.36">
|
||||
<path
|
||||
android:pathData="M107.7,8.07A105.15,105.15 0,0 0,81.47 0a72.06,72.06 0,0 0,-3.36 6.83A97.68,97.68 0,0 0,49 6.83,72.37 72.37,0 0,0 45.64,0 105.89,105.89 0,0 0,19.39 8.09C2.79,32.65 -1.71,56.6 0.54,80.21h0A105.73,105.73 0,0 0,32.71 96.36,77.7 77.7,0 0,0 39.6,85.25a68.42,68.42 0,0 1,-10.85 -5.18c0.91,-0.66 1.8,-1.34 2.66,-2a75.57,75.57 0,0 0,64.32 0c0.87,0.71 1.76,1.39 2.66,2a68.68,68.68 0,0 1,-10.87 5.19,77 77,0 0,0 6.89,11.1A105.25,105.25 0,0 0,126.6 80.22h0C129.24,52.84 122.09,29.11 107.7,8.07ZM42.45,65.69C36.18,65.69 31,60 31,53s5,-12.74 11.43,-12.74S54,46 53.89,53 48.84,65.69 42.45,65.69ZM84.69,65.69C78.41,65.69 73.25,60 73.25,53s5,-12.74 11.44,-12.74S96.23,46 96.12,53 91.08,65.69 84.69,65.69Z"
|
||||
android:fillColor="#fff"/>
|
||||
</vector>
|
||||
10
android/Omnivore/app/src/main/res/drawable/ic_github.xml
Normal file
10
android/Omnivore/app/src/main/res/drawable/ic_github.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="98dp"
|
||||
android:height="96dp"
|
||||
android:viewportWidth="98"
|
||||
android:viewportHeight="96">
|
||||
<path
|
||||
android:pathData="M48.854,0C21.839,0 0,22 0,49.217c0,21.756 13.993,40.172 33.405,46.69 2.427,0.49 3.316,-1.059 3.316,-2.362 0,-1.141 -0.08,-5.052 -0.08,-9.127 -13.59,2.934 -16.42,-5.867 -16.42,-5.867 -2.184,-5.704 -5.42,-7.17 -5.42,-7.17 -4.448,-3.015 0.324,-3.015 0.324,-3.015 4.934,0.326 7.523,5.052 7.523,5.052 4.367,7.496 11.404,5.378 14.235,4.074 0.404,-3.178 1.699,-5.378 3.074,-6.6 -10.839,-1.141 -22.243,-5.378 -22.243,-24.283 0,-5.378 1.94,-9.778 5.014,-13.2 -0.485,-1.222 -2.184,-6.275 0.486,-13.038 0,0 4.125,-1.304 13.426,5.052a46.97,46.97 0,0 1,12.214 -1.63c4.125,0 8.33,0.571 12.213,1.63 9.302,-6.356 13.427,-5.052 13.427,-5.052 2.67,6.763 0.97,11.816 0.485,13.038 3.155,3.422 5.015,7.822 5.015,13.2 0,18.905 -11.404,23.06 -22.324,24.283 1.78,1.548 3.316,4.481 3.316,9.126 0,6.6 -0.08,11.897 -0.08,13.526 0,1.304 0.89,2.853 3.316,2.364 19.412,-6.52 33.405,-24.935 33.405,-46.691C97.707,22 75.788,0 48.854,0z"
|
||||
android:fillColor="#24292f"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
||||
11
android/Omnivore/app/src/main/res/drawable/ic_omnivore.xml
Normal file
11
android/Omnivore/app/src/main/res/drawable/ic_omnivore.xml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:width="256dp"
|
||||
android:height="256dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:pathData="M84.86,53.22C84.86,55.2 84.38,57.6 83.89,59.69V59.71L83.89,59.72C83.29,62.16 81.89,64.33 79.91,65.88C77.93,67.42 75.49,68.26 72.98,68.26H72.84C66.49,68.26 62.49,63.05 62.49,57.21V48.85L57.76,55.86L57.5,56.07C56.52,56.89 55.28,57.34 54,57.34C52.72,57.34 51.48,56.89 50.5,56.07L50.24,55.87L45.37,48.76V66H40.72V47.23C40.72,43.68 44.93,41.61 47.73,44.1L47.94,44.29L53.64,52.61C53.75,52.66 53.87,52.69 53.99,52.69C54.12,52.69 54.23,52.66 54.34,52.61L59.93,44.33L60.18,44.12C62.84,41.91 67.14,43.58 67.14,47.3V57.21C67.14,61 69.55,63.61 72.84,63.61H72.98C76.02,63.61 78.64,61.55 79.37,58.62C79.86,56.54 80.21,54.61 80.21,53.24C80.14,38.26 67.47,26.82 52.13,27.86C39.2,28.75 28.75,39.2 27.85,52.14C27.6,55.72 28.09,59.32 29.3,62.71C30.5,66.1 32.38,69.2 34.84,71.83C37.29,74.45 40.26,76.55 43.55,77.98C46.85,79.41 50.41,80.15 54,80.14V84.79C49.77,84.8 45.58,83.93 41.7,82.24C37.82,80.56 34.33,78.09 31.44,75C28.55,71.91 26.33,68.26 24.91,64.27C23.5,60.28 22.92,56.05 23.21,51.82C24.27,36.56 36.56,24.27 51.81,23.21C69.69,22.01 84.78,35.42 84.86,53.22Z"
|
||||
android:fillColor="#FFFFFF"
|
||||
tools:ignore="VectorPath" />
|
||||
</vector>
|
||||
9
android/Omnivore/app/src/main/res/drawable/ic_x.xml
Normal file
9
android/Omnivore/app/src/main/res/drawable/ic_x.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="1200dp"
|
||||
android:height="1227dp"
|
||||
android:viewportWidth="1200"
|
||||
android:viewportHeight="1227">
|
||||
<path
|
||||
android:pathData="M714.2,519.3L1160.9,0H1055L667.1,450.9L357.3,0H0L468.5,681.8L0,1226.4H105.9L515.5,750.2L842.7,1226.4H1200L714.1,519.3H714.2ZM569.2,687.8L521.7,619.9L144,79.7H306.6L611.4,515.7L658.9,583.6L1055.1,1150.3H892.5L569.2,687.9V687.8Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
||||
|
|
@ -212,6 +212,12 @@
|
|||
<string name="settings_view_setting_row_manage_account">Manage Account</string>
|
||||
<string name="settings_view_setting_row_logout">Logout</string>
|
||||
|
||||
<!-- About -->
|
||||
<string name="about_view_title">About</string>
|
||||
<string name="about_view_row_version">Version</string>
|
||||
<string name="about_view_row_whats_new">What\'s new</string>
|
||||
<string name="website">Website</string>
|
||||
|
||||
<!-- stock filters -->
|
||||
<string name="library_filter_inbox">Inbox</string>
|
||||
<string name="library_filter_non_feed">Non-Feed Items</string>
|
||||
|
|
|
|||
Loading…
Reference in a new issue