diff --git a/android/Omnivore/app/build.gradle b/android/Omnivore/app/build.gradle index 185ef7e89..b5757af1b 100644 --- a/android/Omnivore/app/build.gradle +++ b/android/Omnivore/app/build.gradle @@ -139,12 +139,6 @@ dependencies { implementation "androidx.security:security-crypto:1.0.0" implementation "androidx.datastore:datastore-preferences:1.0.0" - - -// Activity KTX for viewModels() - // TODO: remove this since compose-activity imports ktx? -// implementation "androidx.activity:activity-ktx:1.3.1" - //Dagger - Hilt implementation 'com.google.dagger:hilt-android:2.42' kapt 'com.google.dagger:hilt-compiler:2.42' @@ -156,6 +150,8 @@ dependencies { implementation 'com.google.android.gms:play-services-auth:20.2.0' implementation "com.google.accompanist:accompanist-systemuicontroller:0.25.1" + + implementation("io.coil-kt:coil-compose:2.2.0") } apollo { diff --git a/android/Omnivore/app/src/main/graphql/SaveUrl.graphql b/android/Omnivore/app/src/main/graphql/SaveUrl.graphql index c2a2d2428..964e2b332 100644 --- a/android/Omnivore/app/src/main/graphql/SaveUrl.graphql +++ b/android/Omnivore/app/src/main/graphql/SaveUrl.graphql @@ -1,5 +1,5 @@ -mutation SaveUrl ($input: SaveUrlInput!) { - saveUrl(input:$input){ +mutation SaveUrl($input: SaveUrlInput!) { + saveUrl(input: $input) { ... on SaveSuccess { url } @@ -7,4 +7,4 @@ mutation SaveUrl ($input: SaveUrlInput!) { errorCodes } } -} \ No newline at end of file +} diff --git a/android/Omnivore/app/src/main/graphql/Search.graphql b/android/Omnivore/app/src/main/graphql/Search.graphql new file mode 100644 index 000000000..2532cfc1e --- /dev/null +++ b/android/Omnivore/app/src/main/graphql/Search.graphql @@ -0,0 +1,51 @@ +query Search($after: String, $first: Int, $query: String) { + search(first: $first, after: $after, query: $query) { + ... on SearchSuccess { + edges { + cursor + node { + id + title + slug + url + pageType + contentReader + createdAt + isArchived + readingProgressPercent + readingProgressAnchorIndex + author + image + description + publishedAt + ownedByViewer + originalArticleUrl + uploadFileId + labels { + id + name + color + } + pageId + shortId + quote + annotation + state + siteName + subscription + readAt + } + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + totalCount + } + } + ... on SearchError { + errorCodes + } + } +} diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/MainActivity.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/MainActivity.kt index eed5b92be..274cef68e 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/MainActivity.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/MainActivity.kt @@ -10,6 +10,7 @@ import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import app.omnivore.omnivore.ui.auth.LoginViewModel +import app.omnivore.omnivore.ui.home.HomeViewModel import app.omnivore.omnivore.ui.root.RootView import dagger.hilt.android.AndroidEntryPoint @@ -18,11 +19,12 @@ class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val viewModel: LoginViewModel by viewModels() + val loginViewModel: LoginViewModel by viewModels() + val homeViewModel: HomeViewModel by viewModels() setContent { OmnivoreTheme { - RootView(viewModel = viewModel) + RootView(loginViewModel, homeViewModel) } } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Routes.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Routes.kt index 47f70b2b3..36140cb11 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Routes.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/Routes.kt @@ -1,7 +1,7 @@ package app.omnivore.omnivore sealed class Routes(val route: String) { - object Root : Routes("Root") object Home : Routes("Home") - object Welcome : Routes("Welcome") + object WebReader : Routes("WebReader") + object Settings: Routes("Settings") } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/AppleAuth.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/AppleAuth.kt index ff9ef3b8b..13f9c9626 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/AppleAuth.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/AppleAuth.kt @@ -25,7 +25,7 @@ import java.util.* @Composable fun AppleAuthButton(viewModel: LoginViewModel) { - val showDialog = remember { mutableStateOf(false) } + val showDialog = remember { mutableStateOf(false) } LoadingButtonWithIcon( text = "Continue with Apple", diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/LoginViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/LoginViewModel.kt index 36e5c315f..2b2f682eb 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/LoginViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/LoginViewModel.kt @@ -13,6 +13,7 @@ import com.google.android.gms.tasks.Task import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import javax.inject.Inject @@ -36,6 +37,10 @@ class LoginViewModel @Inject constructor( .distinctUntilChanged() .asLiveData() + fun getAuthCookieString(): String? = runBlocking { + datastoreRepo.getString(DatastoreKeys.omnivoreAuthCookieString) + } + fun login(email: String, password: String) { val emailLogin = RetrofitHelper.getInstance().create(EmailLoginSubmit::class.java) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/WelcomeScreen.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/WelcomeScreen.kt index 455037475..f839361cf 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/WelcomeScreen.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/auth/WelcomeScreen.kt @@ -4,7 +4,6 @@ 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 @@ -19,7 +18,6 @@ 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.text.SpanStyle import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.unit.dp diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt index da1c0e4d9..accd3acec 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeView.kt @@ -1,48 +1,114 @@ package app.omnivore.omnivore.ui.home +import android.annotation.SuppressLint 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.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.TopAppBar +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Menu +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.navigation.NavHostController -import app.omnivore.omnivore.ui.auth.LoginViewModel -import com.google.android.gms.auth.api.signin.GoogleSignIn -import com.google.android.gms.auth.api.signin.GoogleSignInOptions +import app.omnivore.omnivore.Routes +import kotlinx.coroutines.flow.distinctUntilChanged + + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun HomeView( + homeViewModel: HomeViewModel, + navController: NavHostController +) { + Scaffold( + topBar = { + TopAppBar( + title = { Text("Home") }, + backgroundColor = MaterialTheme.colorScheme.surfaceVariant, + actions = { + IconButton(onClick = { navController.navigate(Routes.Settings.route) }) { + Icon( + imageVector = Icons.Default.Menu, + contentDescription = null + ) + } + } + ) + } + ) { paddingValues -> + HomeViewContent( + homeViewModel, + navController, + modifier = Modifier + .padding( + top = paddingValues.calculateTopPadding(), + bottom = paddingValues.calculateBottomPadding() + ) + ) + } +} @Composable -fun HomeView(viewModel: LoginViewModel) { - val context = LocalContext.current +fun HomeViewContent( + homeViewModel: HomeViewModel, + navController: NavHostController, + modifier: Modifier +) { + val listState = rememberLazyListState() + val linkedItems: List by homeViewModel.itemsLiveData.observeAsState(listOf()) - Column( + LazyColumn( + state = listState, verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier + 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 = { - // Sign out google users - val signInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) - .build() - - val googleSignIn = GoogleSignIn.getClient(context, signInOptions) - googleSignIn.signOut() - - viewModel.logout() - }) { - Text(text = "Logout") + items(linkedItems) { item -> + LinkedItemCard( + item = item, + onClickHandler = { + navController.navigate("WebReader/${item.slug}") + } + ) } } + + InfiniteListHandler(listState = listState) { + homeViewModel.load() + } +} + +@Composable +fun InfiniteListHandler( + listState: LazyListState, + buffer: Int = 2, + onLoadMore: () -> Unit +) { + val loadMore = remember { + derivedStateOf { + val layoutInfo = listState.layoutInfo + val totalItemsNumber = layoutInfo.totalItemsCount + val lastVisibleItemIndex = (layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0) + 1 + + lastVisibleItemIndex > (totalItemsNumber - buffer) + } + } + + LaunchedEffect(loadMore) { + snapshotFlow { loadMore.value } + .distinctUntilChanged() + .collect { + onLoadMore() + } + } } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt new file mode 100644 index 000000000..be3f1227a --- /dev/null +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/HomeViewModel.kt @@ -0,0 +1,101 @@ +package app.omnivore.omnivore.ui.home + +import android.annotation.SuppressLint +import android.net.Uri +import android.view.ViewGroup +import android.webkit.WebResourceRequest +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView +import androidx.compose.ui.window.Dialog +import androidx.lifecycle.* +import app.omnivore.omnivore.AppleConstants +import app.omnivore.omnivore.Constants +import app.omnivore.omnivore.DatastoreKeys +import app.omnivore.omnivore.DatastoreRepository +import app.omnivore.omnivore.graphql.generated.SearchQuery +import app.omnivore.omnivore.ui.auth.AppleAuthDialog +import com.apollographql.apollo3.ApolloClient +import com.apollographql.apollo3.api.Optional +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import javax.inject.Inject + +@HiltViewModel +class HomeViewModel @Inject constructor( + private val datastoreRepo: DatastoreRepository +): ViewModel() { + var cursor: String? = null + val itemsLiveData = MutableLiveData>(listOf()) + + private fun getAuthToken(): String? = runBlocking { + datastoreRepo.getString(DatastoreKeys.omnivoreAuthToken) + } + + fun load() { + viewModelScope.launch { + val authToken = getAuthToken() + + val apolloClient = ApolloClient.Builder() + .serverUrl("${Constants.apiURL}/api/graphql") + .addHttpHeader("Authorization", value = authToken ?: "") + .build() + + val response = apolloClient.query( + SearchQuery( + after = Optional.presentIfNotNull(cursor), + first = Optional.presentIfNotNull(15), + ) + ).execute() + + cursor = response.data?.search?.onSearchSuccess?.pageInfo?.endCursor + val itemList = response.data?.search?.onSearchSuccess?.edges ?: listOf() + + val newItems = itemList.map { + LinkedItem( + id = it.node.id, + title = it.node.title, + createdAt = it.node.createdAt, + readAt = it.node.readAt, + readingProgress = it.node.readingProgressPercent, + readingProgressAnchor = it.node.readingProgressAnchorIndex, + imageURLString = it.node.image, + descriptionText = it.node.description, + slug = it.node.slug + ) + } + + itemsLiveData.value = (itemsLiveData.value ?: listOf()).plus(newItems) + } + } +} + +public data class LinkedItem( + public val id: String, + public val title: String, + public val createdAt: Any, +// public val savedAt: Any, + public val readAt: Any?, +// public val updatedAt: Any, + public val readingProgress: Double, + public val readingProgressAnchor: Int, + public val imageURLString: String?, +// public val onDeviceImageURLString: String?, +// public val documentDirectoryPath: String?, +// public val pageURLString: String, + public val descriptionText: String?, +// public val publisherURLString: String?, +// public val siteName: String?, +// public val author: String?, +// public val publishDate: Any?, + public val slug: String, +// public val isArchived: Boolean, +// public val contentReader: String?, +// public val originalHtml: String?, +) diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/LinkedItemCard.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/LinkedItemCard.kt new file mode 100644 index 000000000..c4ab6d4ed --- /dev/null +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/home/LinkedItemCard.kt @@ -0,0 +1,53 @@ +package app.omnivore.omnivore.ui.home + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.Image +import androidx.compose.material3.Card +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import coil.compose.rememberAsyncImagePainter + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun LinkedItemCard( + item: LinkedItem, + onClickHandler: () -> Unit, + modifier: Modifier = Modifier +) { + Card( + onClick = { onClickHandler() }, + shape = MaterialTheme.shapes.medium, + modifier = modifier + .fillMaxWidth() + .padding(15.dp) + ) { + Row { + Image( + painter = rememberAsyncImagePainter(item.imageURLString), + contentDescription = "Image associated with linked item", + modifier = Modifier.size(100.dp) + ) + + Column(modifier = Modifier.padding(8.dp)) { + Text( + text = item.title, + style = MaterialTheme.typography.titleMedium, + maxLines = 2, + overflow = TextOverflow.Ellipsis + ) + + Text( + text = item.descriptionText ?: "", + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.bodyMedium + ) + } + } + } +} diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebAppReader.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebAppReader.kt new file mode 100644 index 000000000..1b1091bbe --- /dev/null +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/reader/WebAppReader.kt @@ -0,0 +1,42 @@ +package app.omnivore.omnivore.ui.reader + +import android.annotation.SuppressLint +import android.view.ViewGroup +import android.webkit.CookieManager +import android.webkit.WebView +import android.webkit.WebViewClient +import androidx.compose.runtime.Composable +import androidx.compose.ui.viewinterop.AndroidView + +@SuppressLint("SetJavaScriptEnabled") +@Composable +fun ArticleWebView(slug: String, authCookieString: String) { + WebView.setWebContentsDebuggingEnabled(true) + + val url = "https://demo.omnivore.app/app/me/$slug" + + AndroidView(factory = { + WebView(it).apply { + layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + + settings.javaScriptEnabled = true + settings.allowContentAccess = true + settings.allowFileAccess = true + settings.domStorageEnabled = true + + webViewClient = object : WebViewClient() { + } + + CookieManager.getInstance().setAcceptThirdPartyCookies(this, true) + CookieManager.getInstance().setAcceptCookie(true) + CookieManager.getInstance().setCookie("https://api-demo.omnivore.app", authCookieString) { + loadUrl(url) + } + } + }, update = { + it.loadUrl(url) + }) +} diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/root/RootView.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/root/RootView.kt index 9bdd36a04..ac8e5e907 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/root/RootView.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/root/RootView.kt @@ -1,19 +1,51 @@ package app.omnivore.omnivore.ui.root +import SettingsView import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import app.omnivore.omnivore.Routes import app.omnivore.omnivore.ui.auth.LoginViewModel import app.omnivore.omnivore.ui.auth.WelcomeScreen import app.omnivore.omnivore.ui.home.HomeView +import app.omnivore.omnivore.ui.home.HomeViewModel +import app.omnivore.omnivore.ui.reader.ArticleWebView @Composable -fun RootView(viewModel: LoginViewModel) { - val hasAuthToken: Boolean by viewModel.hasAuthTokenLiveData.observeAsState(false) +fun RootView(loginViewModel: LoginViewModel, homeViewModel: HomeViewModel) { + val hasAuthToken: Boolean by loginViewModel.hasAuthTokenLiveData.observeAsState(false) - if (hasAuthToken) { - HomeView(viewModel = viewModel) - } else { - WelcomeScreen(viewModel = viewModel) - } + if (hasAuthToken) { + PrimaryNavigator(loginViewModel = loginViewModel, homeViewModel = homeViewModel) + } else { + WelcomeScreen(viewModel = loginViewModel) + } +} + +@Composable +fun PrimaryNavigator(loginViewModel: LoginViewModel, homeViewModel: HomeViewModel){ + val navController = rememberNavController() + + NavHost(navController = navController, startDestination = Routes.Home.route) { + composable(Routes.Home.route) { + HomeView( + homeViewModel = homeViewModel, + navController = navController + ) + } + + composable("WebReader/{slug}") { + ArticleWebView( + it.arguments?.getString("slug") ?: "", + authCookieString = loginViewModel.getAuthCookieString() ?: "" + ) + } + + composable(Routes.Settings.route) { + SettingsView(loginViewModel = loginViewModel, navController = navController) + } + } } diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/save/SaveViewModel.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/save/SaveViewModel.kt index 62a11b9ac..ba706a0cd 100644 --- a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/save/SaveViewModel.kt +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/save/SaveViewModel.kt @@ -40,8 +40,6 @@ class SaveViewModel @Inject constructor( val authToken = getAuthToken() - Log.d(ContentValues.TAG, "AuthToken: $authToken") - if (authToken == null) { message = "You are not logged in. Please login before saving." isLoading = false diff --git a/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/settings/SettingsContent.kt b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/settings/SettingsContent.kt new file mode 100644 index 000000000..64d0b4bf8 --- /dev/null +++ b/android/Omnivore/app/src/main/java/app/omnivore/omnivore/ui/settings/SettingsContent.kt @@ -0,0 +1,73 @@ +import android.annotation.SuppressLint +import androidx.compose.foundation.layout.* +import androidx.compose.material.TopAppBar +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Home +import androidx.compose.material.icons.filled.Menu +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +import app.omnivore.omnivore.Routes +import app.omnivore.omnivore.ui.auth.LoginViewModel +import app.omnivore.omnivore.ui.home.HomeViewContent +import app.omnivore.omnivore.ui.home.HomeViewModel +import com.google.android.gms.auth.api.signin.GoogleSignIn +import com.google.android.gms.auth.api.signin.GoogleSignInOptions + +@OptIn(ExperimentalMaterial3Api::class) +@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") +@Composable +fun SettingsView( + loginViewModel: LoginViewModel, + navController: NavHostController +) { + Scaffold( + topBar = { + TopAppBar( + title = { Text("Settings") }, + backgroundColor = MaterialTheme.colorScheme.surfaceVariant, + actions = { + IconButton(onClick = { navController.navigate(Routes.Home.route) }) { + Icon( + imageVector = Icons.Default.Home, + contentDescription = null + ) + } + } + ) + } + ) { + Column( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .fillMaxSize() + .navigationBarsPadding() + .padding(horizontal = 16.dp) + ) { + LogoutButton { loginViewModel.logout() } + } + } +} + +@Composable +fun LogoutButton(actionHandler: () -> Unit) { + val context = LocalContext.current + + Button(onClick = { + // Sign out google users + val signInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) + .build() + + val googleSignIn = GoogleSignIn.getClient(context, signInOptions) + googleSignIn.signOut() + + actionHandler() + }) { + Text(text = "Logout") + } +}