From e8604a5c0d771501d2facecda60da1a98593872d Mon Sep 17 00:00:00 2001 From: itmani1990 Date: Sun, 6 Oct 2024 15:56:16 -0500 Subject: [PATCH 01/12] Features added - lambda func like CopyText & ShareAction events moved into MainEvents --- .../com/mani/quotify007/ui/MainActivity.kt | 10 +++++++--- .../quotify007/ui/navigation/MainScreen.kt | 13 +++--------- .../ui/navigation/NavigationGraph.kt | 20 +++++-------------- .../ui/navigation/model/MainEvent.kt | 2 ++ .../ui/navigation/viewmodel/MainViewModel.kt | 11 ++++++++++ .../ui/screens/favorites/FavoritesScreen.kt | 15 +++++--------- .../quotify007/ui/screens/home/HomeScreen.kt | 13 ++++-------- .../ui/screens/quote/QuotesScreen.kt | 13 +++++------- .../ui/screens/search/SearchScreen.kt | 14 ++++--------- 9 files changed, 46 insertions(+), 65 deletions(-) diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/MainActivity.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/MainActivity.kt index 8db4517..ef6298f 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/MainActivity.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/MainActivity.kt @@ -23,11 +23,15 @@ class MainActivity : ComponentActivity() { val state = viewModel.state.collectAsState().value MainScreen( state, - onEvent = { event -> viewModel.onEvent(event) }, - onCopyText = { quote -> onCopyText(this, quote) }, - onShareClick = { quote -> shareQuote(this, quote) } + onEvent = { event -> viewModel.onEvent(event) } ) } } + viewModel.copyTextEvent.observe(this) { quote -> + onCopyText(this, quote) + } + viewModel.shareClickEvent.observe(this) { quote -> + shareQuote(this, quote) + } } } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/MainScreen.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/MainScreen.kt index 77c9758..902b655 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/MainScreen.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/MainScreen.kt @@ -6,7 +6,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.navigation.compose.rememberNavController -import com.mani.quotify007.domain.model.Quote import com.mani.quotify007.ui.navigation.model.MainEvent import com.mani.quotify007.ui.navigation.model.MainState import com.mani.quotify007.ui.screens.bottomappbar.BottomAppBar @@ -15,9 +14,7 @@ import com.mani.quotify007.ui.theme.QuotifyAppTheme @Composable fun MainScreen( state: MainState, - onEvent: (MainEvent) -> Unit, - onCopyText: (Quote) -> Unit, - onShareClick: (Quote) -> Unit + onEvent: (MainEvent) -> Unit ) { val navController = rememberNavController() Scaffold( @@ -29,9 +26,7 @@ fun MainScreen( state = state, quotes = state.quotes, favoriteQuotes = state.favoriteQuotes, - onEvent = onEvent, - onCopyText = onCopyText, - onShareClick = onShareClick + onEvent = onEvent ) } } @@ -42,9 +37,7 @@ fun MainScreenPreview() { QuotifyAppTheme { MainScreen( state = MainState(), - onEvent = {}, - onCopyText = {}, - onShareClick = {} + onEvent = {} ) } } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/NavigationGraph.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/NavigationGraph.kt index a1eeec4..087fccf 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/NavigationGraph.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/NavigationGraph.kt @@ -22,33 +22,25 @@ fun NavigationGraph( state: MainState, quotes: List, favoriteQuotes: List, - onEvent: (MainEvent) -> Unit, - onCopyText: (Quote) -> Unit, - onShareClick: (Quote) -> Unit + onEvent: (MainEvent) -> Unit ) { NavHost(navController, startDestination = BottomNavItem.HOME.route, modifier = modifier) { composable(BottomNavItem.HOME.route) { HomeScreen( state, - onEvent, - onCopyText, - onShareClick + onEvent ) } composable(BottomNavItem.SEARCH.route) { SearchScreen( quotes, - onEvent, - onCopyText, - onShareClick + onEvent ) } composable(BottomNavItem.FAVORITES.route) { FavoritesScreen( favoriteQuotes, - onEvent, - onCopyText, - onShareClick + onEvent ) } } @@ -63,8 +55,6 @@ fun NavigationGraphPreview() { state = MainState(), quotes = listOf(Quote(0,"Sample quote", "Sample Author")), favoriteQuotes = listOf(Quote(0,"Sample favorite quote", "Sample Author")), - onEvent = {}, - onCopyText = {}, - onShareClick = {} + onEvent = {} ) } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/model/MainEvent.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/model/MainEvent.kt index 12bae21..e639fa8 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/model/MainEvent.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/model/MainEvent.kt @@ -6,4 +6,6 @@ sealed class MainEvent { data class AddFavorite(val quote: Quote) : MainEvent() data class RemoveFavorite(val quote: Quote): MainEvent() data object GetRandomQuote: MainEvent() + data class CopyText(val quote: Quote): MainEvent() + data class ShareClick(val quote: Quote): MainEvent() } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt index 263b902..7b48425 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt @@ -2,6 +2,8 @@ package com.mani.quotify007.ui.navigation.viewmodel import android.app.Application import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.mani.quotify007.QuotifyApp import com.mani.quotify007.data.local.FavoriteQuoteEntity @@ -16,6 +18,12 @@ class MainViewModel(private val application: Application) : AndroidViewModel(app private val _state = MutableStateFlow(MainState()) val state: StateFlow = _state + private val _copyTextEvent = MutableLiveData() + val copyTextEvent: LiveData = _copyTextEvent + + private val _shareClickEvent = MutableLiveData() + val shareClickEvent: LiveData = _shareClickEvent + private val getQuoteUseCase = (application as QuotifyApp).getQuoteUseCase init { @@ -60,6 +68,9 @@ class MainViewModel(private val application: Application) : AndroidViewModel(app val randomQuote = _state.value.quotes.randomOrNull() _state.value = _state.value.copy(randomQuote = randomQuote) } + + is MainEvent.CopyText -> _copyTextEvent.postValue(event.quote) + is MainEvent.ShareClick -> _shareClickEvent.postValue(event.quote) } } } diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/favorites/FavoritesScreen.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/favorites/FavoritesScreen.kt index 0d9899d..54f6c6d 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/favorites/FavoritesScreen.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/favorites/FavoritesScreen.kt @@ -19,9 +19,7 @@ import com.mani.quotify007.ui.screens.quote.QuotesScreen @Composable fun FavoritesScreen( quotes: List, - onEvent: (MainEvent) -> Unit, - onCopyText: (Quote) -> Unit, - onShareClick: (Quote) -> Unit + onEvent: (MainEvent) -> Unit ) { if (quotes.isEmpty()) { Column( @@ -44,10 +42,8 @@ fun FavoritesScreen( items(quotes) { quote -> QuotesScreen( quote, - onEvent = { onEvent(MainEvent.RemoveFavorite(quote)) }, - false, - onCopyText = onCopyText, - onShareClick = onShareClick + onEvent = onEvent, + false ) } } @@ -60,7 +56,6 @@ fun FavoritesScreen( fun FavoritesScreenPreview() { FavoritesScreen( listOf(Quote(0, "Sample quote", "Sample Author")), - onEvent = {}, - onCopyText = {}, - onShareClick = {}) + onEvent = {} + ) } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/home/HomeScreen.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/home/HomeScreen.kt index b2e36a3..0392db8 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/home/HomeScreen.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/home/HomeScreen.kt @@ -19,7 +19,6 @@ import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.mani.quotify007.domain.model.Quote import com.mani.quotify007.ui.navigation.model.MainEvent import com.mani.quotify007.ui.navigation.model.MainState import com.mani.quotify007.ui.screens.quote.QuotesScreen @@ -28,9 +27,7 @@ import com.mani.quotify007.ui.screens.utils.QUOTE_OF_THE_DAY_HEADER @Composable fun HomeScreen( state: MainState, - onEvent: (MainEvent) -> Unit, - onCopyText: (Quote) -> Unit, - onShareClick: (Quote) -> Unit + onEvent: (MainEvent) -> Unit ) { Column( modifier = Modifier @@ -43,10 +40,8 @@ fun HomeScreen( state.randomQuote?.let { quote -> QuotesScreen( quote = quote, - onEvent = { onEvent(MainEvent.AddFavorite(quote)) }, - isAddOnly = true, - onCopyText = onCopyText, - onShareClick = onShareClick + onEvent = onEvent, + isAddOnly = true ) } Spacer(modifier = Modifier.height(16.dp)) @@ -61,5 +56,5 @@ fun HomeScreen( @Preview(showBackground = true) @Composable fun HomeScreenPreview() { - HomeScreen(MainState(), onEvent = {}, onCopyText = {}, onShareClick = {}) + HomeScreen(MainState(), onEvent = {}) } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/quote/QuotesScreen.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/quote/QuotesScreen.kt index cd3aa34..4c02ee5 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/quote/QuotesScreen.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/quote/QuotesScreen.kt @@ -36,9 +36,7 @@ import com.mani.quotify007.ui.screens.utils.HYPHEN_SPACE fun QuotesScreen( quote: Quote, onEvent: (MainEvent) -> Unit, - isAddOnly: Boolean, - onCopyText: (Quote) -> Unit, - onShareClick: (Quote) -> Unit + isAddOnly: Boolean ) { // TODO: Approach to be discussed - context should be passed from MainActivity. val context = LocalContext.current @@ -102,13 +100,13 @@ fun QuotesScreen( Text("Save") } TextButton(onClick = { - onCopyText(quote) + onEvent(MainEvent.CopyText(quote)) }) { Spacer(modifier = Modifier.width(4.dp)) Text("Copy Text") } TextButton(onClick = { - onShareClick(quote) + onEvent(MainEvent.ShareClick(quote)) }) { Icon(imageVector = Icons.Default.Share, contentDescription = "Share") Spacer(modifier = Modifier.width(4.dp)) @@ -124,7 +122,6 @@ fun QuotesScreenPreview() { QuotesScreen( Quote(0, "Sample quote", "Sample author"), onEvent = {}, - true, - onCopyText = {}, - onShareClick = {}) + true + ) } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/search/SearchScreen.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/search/SearchScreen.kt index 4eadd80..16f3249 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/search/SearchScreen.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/search/SearchScreen.kt @@ -32,9 +32,7 @@ import com.mani.quotify007.ui.screens.quote.QuotesScreen @Composable fun SearchScreen( quotes: List, - onEvent: (MainEvent) -> Unit, - onCopyText: (Quote) -> Unit, - onShareClick: (Quote) -> Unit + onEvent: (MainEvent) -> Unit ) { var searchQuery by remember { mutableStateOf(TextFieldValue("")) } val filteredQuotes = quotes.filter { it.text.contains(searchQuery.text, ignoreCase = true) } @@ -63,10 +61,8 @@ fun SearchScreen( items(filteredQuotes) { quote -> QuotesScreen( quote, - onEvent = { onEvent(MainEvent.AddFavorite(quote)) }, - true, - onCopyText = onCopyText, - onShareClick = onShareClick + onEvent = onEvent, + true ) } } @@ -78,8 +74,6 @@ fun SearchScreen( fun SearchScreenPreview() { SearchScreen( listOf(Quote(0, "Sample quote")), - onEvent = {}, - onCopyText = {}, - onShareClick = {} + onEvent = {} ) } \ No newline at end of file From 5610a3e105ba2a8128e32f71af0f6de5178c3b12 Mon Sep 17 00:00:00 2001 From: itmani1990 Date: Sun, 6 Oct 2024 16:04:33 -0500 Subject: [PATCH 02/12] Features added - state passed into screens instead data --- .../com/mani/quotify007/ui/navigation/MainScreen.kt | 2 -- .../mani/quotify007/ui/navigation/NavigationGraph.kt | 9 ++------- .../quotify007/ui/screens/favorites/FavoritesScreen.kt | 10 +++++----- .../mani/quotify007/ui/screens/search/SearchScreen.kt | 9 +++++---- 4 files changed, 12 insertions(+), 18 deletions(-) diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/MainScreen.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/MainScreen.kt index 902b655..ea9e170 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/MainScreen.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/MainScreen.kt @@ -24,8 +24,6 @@ fun MainScreen( navController = navController, modifier = Modifier.padding(innerPadding), state = state, - quotes = state.quotes, - favoriteQuotes = state.favoriteQuotes, onEvent = onEvent ) } diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/NavigationGraph.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/NavigationGraph.kt index 087fccf..c6f9d06 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/NavigationGraph.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/NavigationGraph.kt @@ -7,7 +7,6 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController -import com.mani.quotify007.domain.model.Quote import com.mani.quotify007.ui.navigation.model.MainEvent import com.mani.quotify007.ui.navigation.model.MainState import com.mani.quotify007.ui.screens.bottomappbar.BottomNavItem @@ -20,8 +19,6 @@ fun NavigationGraph( navController: NavHostController, modifier: Modifier = Modifier, state: MainState, - quotes: List, - favoriteQuotes: List, onEvent: (MainEvent) -> Unit ) { NavHost(navController, startDestination = BottomNavItem.HOME.route, modifier = modifier) { @@ -33,13 +30,13 @@ fun NavigationGraph( } composable(BottomNavItem.SEARCH.route) { SearchScreen( - quotes, + state, onEvent ) } composable(BottomNavItem.FAVORITES.route) { FavoritesScreen( - favoriteQuotes, + state, onEvent ) } @@ -53,8 +50,6 @@ fun NavigationGraphPreview() { NavigationGraph( navController = navController, state = MainState(), - quotes = listOf(Quote(0,"Sample quote", "Sample Author")), - favoriteQuotes = listOf(Quote(0,"Sample favorite quote", "Sample Author")), onEvent = {} ) } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/favorites/FavoritesScreen.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/favorites/FavoritesScreen.kt index 54f6c6d..083309f 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/favorites/FavoritesScreen.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/favorites/FavoritesScreen.kt @@ -12,16 +12,16 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import com.mani.quotify007.domain.model.Quote import com.mani.quotify007.ui.navigation.model.MainEvent +import com.mani.quotify007.ui.navigation.model.MainState import com.mani.quotify007.ui.screens.quote.QuotesScreen @Composable fun FavoritesScreen( - quotes: List, + state: MainState, onEvent: (MainEvent) -> Unit ) { - if (quotes.isEmpty()) { + if (state.favoriteQuotes.isEmpty()) { Column( modifier = Modifier .fillMaxSize() @@ -39,7 +39,7 @@ fun FavoritesScreen( horizontalAlignment = Alignment.CenterHorizontally ) { LazyColumn { - items(quotes) { quote -> + items(state.favoriteQuotes) { quote -> QuotesScreen( quote, onEvent = onEvent, @@ -55,7 +55,7 @@ fun FavoritesScreen( @Composable fun FavoritesScreenPreview() { FavoritesScreen( - listOf(Quote(0, "Sample quote", "Sample Author")), + MainState(), onEvent = {} ) } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/search/SearchScreen.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/search/SearchScreen.kt index 16f3249..369a5ab 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/search/SearchScreen.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/search/SearchScreen.kt @@ -25,17 +25,18 @@ import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import com.mani.quotify007.domain.model.Quote import com.mani.quotify007.ui.navigation.model.MainEvent +import com.mani.quotify007.ui.navigation.model.MainState import com.mani.quotify007.ui.screens.quote.QuotesScreen @Composable fun SearchScreen( - quotes: List, + state: MainState, onEvent: (MainEvent) -> Unit ) { var searchQuery by remember { mutableStateOf(TextFieldValue("")) } - val filteredQuotes = quotes.filter { it.text.contains(searchQuery.text, ignoreCase = true) } + val filteredQuotes = + state.quotes.filter { it.text.contains(searchQuery.text, ignoreCase = true) } Column(modifier = Modifier .fillMaxSize() @@ -73,7 +74,7 @@ fun SearchScreen( @Composable fun SearchScreenPreview() { SearchScreen( - listOf(Quote(0, "Sample quote")), + MainState(), onEvent = {} ) } \ No newline at end of file From f7963cd7479ece25d64185c2e9beb5b99bff68b5 Mon Sep 17 00:00:00 2001 From: itmani1990 Date: Sun, 6 Oct 2024 16:26:11 -0500 Subject: [PATCH 03/12] Features added - Toast handled with extension func and event --- .../java/com/mani/quotify007/ui/MainActivity.kt | 4 ++++ .../quotify007/ui/navigation/model/MainEvent.kt | 1 + .../ui/navigation/viewmodel/MainViewModel.kt | 4 ++++ .../quotify007/ui/screens/quote/QuotesScreen.kt | 15 ++++++++------- .../quotify007/ui/screens/utils/ClipboardUtil.kt | 2 -- .../ui/screens/utils/ContextExtensions.kt | 8 ++++++++ .../quotify007/ui/screens/utils/StringUtil.kt | 6 +++++- 7 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ContextExtensions.kt diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/MainActivity.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/MainActivity.kt index ef6298f..4d739fc 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/MainActivity.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/MainActivity.kt @@ -9,6 +9,7 @@ import com.mani.quotify007.ui.navigation.MainScreen import com.mani.quotify007.ui.navigation.viewmodel.MainViewModel import com.mani.quotify007.ui.screens.utils.onCopyText import com.mani.quotify007.ui.screens.utils.shareQuote +import com.mani.quotify007.ui.screens.utils.showToast import com.mani.quotify007.ui.theme.QuotifyAppTheme class MainActivity : ComponentActivity() { @@ -33,5 +34,8 @@ class MainActivity : ComponentActivity() { viewModel.shareClickEvent.observe(this) { quote -> shareQuote(this, quote) } + viewModel.showToast.observe(this) { message -> + message?.let { showToast(it) } + } } } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/model/MainEvent.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/model/MainEvent.kt index e639fa8..0fac455 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/model/MainEvent.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/model/MainEvent.kt @@ -8,4 +8,5 @@ sealed class MainEvent { data object GetRandomQuote: MainEvent() data class CopyText(val quote: Quote): MainEvent() data class ShareClick(val quote: Quote): MainEvent() + data class ShowToast(val message: String): MainEvent() } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt index 7b48425..48305e8 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt @@ -24,6 +24,9 @@ class MainViewModel(private val application: Application) : AndroidViewModel(app private val _shareClickEvent = MutableLiveData() val shareClickEvent: LiveData = _shareClickEvent + private val _showToast = MutableLiveData() + val showToast: LiveData = _showToast + private val getQuoteUseCase = (application as QuotifyApp).getQuoteUseCase init { @@ -71,6 +74,7 @@ class MainViewModel(private val application: Application) : AndroidViewModel(app is MainEvent.CopyText -> _copyTextEvent.postValue(event.quote) is MainEvent.ShareClick -> _shareClickEvent.postValue(event.quote) + is MainEvent.ShowToast -> _showToast.postValue(event.message) } } } diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/quote/QuotesScreen.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/quote/QuotesScreen.kt index 4c02ee5..9730016 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/quote/QuotesScreen.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/quote/QuotesScreen.kt @@ -1,6 +1,5 @@ package com.mani.quotify007.ui.screens.quote -import android.widget.Toast import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -21,7 +20,6 @@ import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign @@ -30,7 +28,11 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.mani.quotify007.domain.model.Quote import com.mani.quotify007.ui.navigation.model.MainEvent +import com.mani.quotify007.ui.screens.utils.ADDED_TO_FAVORITES +import com.mani.quotify007.ui.screens.utils.ALREADY_ADDED_TO_FAVORITES import com.mani.quotify007.ui.screens.utils.HYPHEN_SPACE +import com.mani.quotify007.ui.screens.utils.REMOVED_FROM_FAVORITES +import com.mani.quotify007.ui.screens.utils.TEXT_COPIED_TO_CLIPBOARD @Composable fun QuotesScreen( @@ -38,8 +40,6 @@ fun QuotesScreen( onEvent: (MainEvent) -> Unit, isAddOnly: Boolean ) { - // TODO: Approach to be discussed - context should be passed from MainActivity. - val context = LocalContext.current Card( modifier = Modifier .fillMaxWidth() @@ -80,15 +80,15 @@ fun QuotesScreen( if (!quote.isFavorite) { quote.isFavorite = true onEvent(MainEvent.AddFavorite(quote)) - Toast.makeText(context, "Added to favorites", Toast.LENGTH_SHORT).show() + onEvent(MainEvent.ShowToast(ADDED_TO_FAVORITES)) } else { - Toast.makeText(context, "Already added to favorites", Toast.LENGTH_SHORT).show() + onEvent(MainEvent.ShowToast(ALREADY_ADDED_TO_FAVORITES)) } } else { if (quote.isFavorite) { quote.isFavorite = false onEvent(MainEvent.RemoveFavorite(quote)) - Toast.makeText(context, "Removed from favorites", Toast.LENGTH_SHORT).show() + onEvent(MainEvent.ShowToast(REMOVED_FROM_FAVORITES)) } } }) { @@ -101,6 +101,7 @@ fun QuotesScreen( } TextButton(onClick = { onEvent(MainEvent.CopyText(quote)) + onEvent(MainEvent.ShowToast(TEXT_COPIED_TO_CLIPBOARD)) }) { Spacer(modifier = Modifier.width(4.dp)) Text("Copy Text") diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ClipboardUtil.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ClipboardUtil.kt index 7b2e051..d5fcc23 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ClipboardUtil.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ClipboardUtil.kt @@ -3,7 +3,6 @@ package com.mani.quotify007.ui.screens.utils import android.content.ClipData import android.content.ClipboardManager import android.content.Context -import android.widget.Toast import com.mani.quotify007.domain.model.Quote fun onCopyText(context: Context, quote: Quote) { @@ -15,5 +14,4 @@ fun onCopyText(context: Context, quote: Quote) { "${quote.text} - ${quote.author}" ) ) - Toast.makeText(context, "Text copied to clipboard", Toast.LENGTH_SHORT).show() } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ContextExtensions.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ContextExtensions.kt new file mode 100644 index 0000000..b9151b2 --- /dev/null +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ContextExtensions.kt @@ -0,0 +1,8 @@ +package com.mani.quotify007.ui.screens.utils + +import android.content.Context +import android.widget.Toast + +fun Context.showToast(message: String) { + Toast.makeText(this, message, Toast.LENGTH_SHORT).show() +} \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/StringUtil.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/StringUtil.kt index 81cfe84..2663a24 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/StringUtil.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/StringUtil.kt @@ -1,4 +1,8 @@ package com.mani.quotify007.ui.screens.utils const val QUOTE_OF_THE_DAY_HEADER = "Quote of the Day" -const val HYPHEN_SPACE = "- " \ No newline at end of file +const val HYPHEN_SPACE = "- " +const val ALREADY_ADDED_TO_FAVORITES = "Already added to favorites" +const val ADDED_TO_FAVORITES = "Added to favorites" +const val REMOVED_FROM_FAVORITES = "Removed from favorites" +const val TEXT_COPIED_TO_CLIPBOARD = "Text copied to clipboard" \ No newline at end of file From 8e7d1d26883ad274c212a4d8153fabb719f96efb Mon Sep 17 00:00:00 2001 From: itmani1990 Date: Sun, 6 Oct 2024 21:03:35 -0500 Subject: [PATCH 04/12] Features added - Remote call implementation --- Mani-Quotify/app/build.gradle.kts | 2 + Mani-Quotify/app/src/main/AndroidManifest.xml | 1 + .../java/com/mani/quotify007/QuotifyApp.kt | 15 ++++- .../data/local/FavoriteQuoteEntity.kt | 4 +- .../quotify007/data/remote/QuoteApiService.kt | 9 +++ .../data/remote/getSafeOkHttpClient.kt | 40 +++++++++++++ .../data/repository/QuoteRepositoryImpl.kt | 60 +++++-------------- .../data/repository/QuoteStaticData.kt | 39 ++++++++++++ .../com/mani/quotify007/domain/model/Quote.kt | 11 +++- .../quotify007/domain/model/QuoteResult.kt | 10 ++++ .../domain/repository/QuoteRepository.kt | 2 + .../domain/usecase/GetQuoteUseCase.kt | 3 + .../quotify007/ui/navigation/MainScreen.kt | 20 +++++-- .../ui/navigation/model/MainState.kt | 3 +- .../ui/navigation/viewmodel/MainViewModel.kt | 28 +++++++-- .../quotify007/ui/screens/home/HomeScreen.kt | 3 +- .../ui/screens/quote/QuotesScreen.kt | 4 +- .../ui/screens/search/SearchScreen.kt | 2 +- .../ui/screens/utils/ClipboardUtil.kt | 2 +- .../quotify007/ui/screens/utils/ShareUtil.kt | 2 +- .../app/src/main/res/raw/api_quotable_io.pem | 29 +++++++++ Mani-Quotify/gradle/libs.versions.toml | 4 ++ 22 files changed, 223 insertions(+), 70 deletions(-) create mode 100644 Mani-Quotify/app/src/main/java/com/mani/quotify007/data/remote/QuoteApiService.kt create mode 100644 Mani-Quotify/app/src/main/java/com/mani/quotify007/data/remote/getSafeOkHttpClient.kt create mode 100644 Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteStaticData.kt create mode 100644 Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/model/QuoteResult.kt create mode 100644 Mani-Quotify/app/src/main/res/raw/api_quotable_io.pem diff --git a/Mani-Quotify/app/build.gradle.kts b/Mani-Quotify/app/build.gradle.kts index 831ea3c..ee1188d 100644 --- a/Mani-Quotify/app/build.gradle.kts +++ b/Mani-Quotify/app/build.gradle.kts @@ -63,6 +63,8 @@ dependencies { implementation(libs.androidx.ui.navigation) implementation(libs.androidx.room.runtime) implementation(libs.androidx.room.ktx) + implementation(libs.retrofit) + implementation(libs.converter.gson) ksp(libs.androidx.room.compiler) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) diff --git a/Mani-Quotify/app/src/main/AndroidManifest.xml b/Mani-Quotify/app/src/main/AndroidManifest.xml index 5a168bd..deb8f01 100644 --- a/Mani-Quotify/app/src/main/AndroidManifest.xml +++ b/Mani-Quotify/app/src/main/AndroidManifest.xml @@ -2,6 +2,7 @@ + + val certificateFactory = java.security.cert.CertificateFactory.getInstance("X.509") + val certificate = certificateFactory.generateCertificate(certInputStream) + setCertificateEntry("api_quotable_io", certificate) + } + } + + // Create a TrustManager that trusts the CAs in our KeyStore + val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()).apply { + init(keyStore) + } + val trustManagers = trustManagerFactory.trustManagers + val sslContext = SSLContext.getInstance("TLS").apply { + init(null, trustManagers, java.security.SecureRandom()) + } + val sslSocketFactory = sslContext.socketFactory + + OkHttpClient.Builder() + .sslSocketFactory(sslSocketFactory, trustManagers[0] as X509TrustManager) + .build() + } catch (e: Exception) { + throw RuntimeException(e) + } +} \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteRepositoryImpl.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteRepositoryImpl.kt index adac15b..4d2edd6 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteRepositoryImpl.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteRepositoryImpl.kt @@ -1,73 +1,43 @@ package com.mani.quotify007.data.repository -import com.mani.quotify007.data.local.FavoriteQuoteDao +import com.mani.quotify007.QuotifyApp import com.mani.quotify007.data.local.FavoriteQuoteEntity import com.mani.quotify007.domain.model.Quote +import com.mani.quotify007.domain.model.QuoteResult import com.mani.quotify007.domain.repository.QuoteRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -class QuoteRepositoryImpl(private val favoriteQuoteDao: FavoriteQuoteDao) : QuoteRepository { - private val quotes = mutableListOf( - Quote( - 1, - "The greatest glory in living lies not in never falling, " + - "but in rising every time we fall.", - "Nelson Mandela" - ), - Quote( - 2, - "The way to get started is to quit talking and begin doing.", - "Walt Disney" - ), - Quote( - 3, - "Your time is limited, so don't waste it living someone else's life.", - "Steve Jobs" - ), - Quote( - 4, - "If life were predictable it would cease to be life, and be without flavor.", - "Eleanor Roosevelt" - ), - Quote( - 5, - "If you look at what you have in life, you'll always have more. " + - "If you look at what you don't have in life, you'll never have enough.", - "Oprah Winfrey" - ), - Quote( - 6, - "If you set your goals ridiculously high and it's a failure, " + - "you will fail above everyone else's success.", - "James Cameron" - ) - ) +class QuoteRepositoryImpl(private val application: QuotifyApp) : QuoteRepository { - override fun getQuotes(): List = quotes + override suspend fun getQuoteResult(): QuoteResult = application.api.getQuoteResult() + override fun getQuotes(): List = quotesDataList + + //TODO: Future implementation override fun addQuote(quote: Quote) { - quotes.add(quote) + quotesDataList.add(quote) } + //TODO: Future implementation override fun removeQuote(quote: Quote) { - quotes.remove(quote) + quotesDataList.remove(quote) } override fun getFavoriteQuotes(): Flow> = - favoriteQuoteDao.getAllFavoriteQuotes().map {entities -> + application.quoteDb.favoriteQuoteDao().getAllFavoriteQuotes().map {entities -> entities.map { entity -> entity.toDomainModel() } } override suspend fun addFavoriteQuote(quote: Quote) { - favoriteQuoteDao.insertFavoriteQuote(quote.toEntity()) + application.quoteDb.favoriteQuoteDao().insertFavoriteQuote(quote.toEntity()) } override suspend fun removeFavoriteQuote(quote: Quote) { - favoriteQuoteDao.deleteFavoriteQuote(quote.toEntity()) + application.quoteDb.favoriteQuoteDao().deleteFavoriteQuote(quote.toEntity()) } } -private fun Quote.toEntity() = FavoriteQuoteEntity(id = id, text = text, author = author ?: "") +private fun Quote.toEntity() = FavoriteQuoteEntity(id = _id, content = content, author = author) -private fun FavoriteQuoteEntity.toDomainModel() = Quote(id, text, author) +private fun FavoriteQuoteEntity.toDomainModel() = Quote(id, content = content, author = author) diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteStaticData.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteStaticData.kt new file mode 100644 index 0000000..208641c --- /dev/null +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteStaticData.kt @@ -0,0 +1,39 @@ +package com.mani.quotify007.data.repository + +import com.mani.quotify007.domain.model.Quote + +val quotesDataList = mutableListOf( + Quote( + _id = "111", + content = "The greatest glory in living lies not in never falling, " + + "but in rising every time we fall.", + author = "Nelson Mandela" + ), + Quote( + _id = "222", + content = "The way to get started is to quit talking and begin doing.", + author = "Walt Disney" + ), + Quote( + _id = "333", + content = "Your time is limited, so don't waste it living someone else's life.", + author = "Steve Jobs" + ), + Quote( + _id = "444", + content = "If life were predictable it would cease to be life, and be without flavor.", + author = "Eleanor Roosevelt" + ), + Quote( + _id = "555", + content = "If you look at what you have in life, you'll always have more. " + + "If you look at what you don't have in life, you'll never have enough.", + author = "Oprah Winfrey" + ), + Quote( + _id = "666", + content = "If you set your goals ridiculously high and it's a failure, " + + "you will fail above everyone else's success.", + author = "James Cameron" + ) +) \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/model/Quote.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/model/Quote.kt index 9eb3074..9fca7d1 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/model/Quote.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/model/Quote.kt @@ -1,8 +1,13 @@ package com.mani.quotify007.domain.model data class Quote( - val id: Int, - val text: String, - val author: String? = null, + val _id: String, + val author: String, + val authorSlug: String? = null, + val content: String, + val dateAdded: String? = null, + val dateModified: String? = null, + val length: Int? = null, + val tags: List? = null, var isFavorite: Boolean = false ) diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/model/QuoteResult.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/model/QuoteResult.kt new file mode 100644 index 0000000..d118ac8 --- /dev/null +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/model/QuoteResult.kt @@ -0,0 +1,10 @@ +package com.mani.quotify007.domain.model + +data class QuoteResult( + val count: Int, + val lastItemIndex: Int, + val page: Int, + val results: List, + val totalCount: Int, + val totalPages: Int +) \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/repository/QuoteRepository.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/repository/QuoteRepository.kt index cdddc6c..fbcf3c9 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/repository/QuoteRepository.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/repository/QuoteRepository.kt @@ -1,9 +1,11 @@ package com.mani.quotify007.domain.repository import com.mani.quotify007.domain.model.Quote +import com.mani.quotify007.domain.model.QuoteResult import kotlinx.coroutines.flow.Flow interface QuoteRepository { + suspend fun getQuoteResult(): QuoteResult fun getQuotes(): List fun addQuote(quote: Quote) fun removeQuote(quote: Quote) diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/usecase/GetQuoteUseCase.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/usecase/GetQuoteUseCase.kt index c01d784..2d59458 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/usecase/GetQuoteUseCase.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/usecase/GetQuoteUseCase.kt @@ -1,8 +1,11 @@ package com.mani.quotify007.domain.usecase import com.mani.quotify007.domain.model.Quote +import com.mani.quotify007.domain.model.QuoteResult import com.mani.quotify007.domain.repository.QuoteRepository class GetQuoteUseCase(private val repository: QuoteRepository) { + suspend fun result(): QuoteResult = repository.getQuoteResult() + // TODO: Use when static quote data used fun execute(): List = repository.getQuotes() } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/MainScreen.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/MainScreen.kt index ea9e170..d5dcb2b 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/MainScreen.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/MainScreen.kt @@ -1,8 +1,11 @@ package com.mani.quotify007.ui.navigation +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.padding +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.navigation.compose.rememberNavController @@ -20,12 +23,17 @@ fun MainScreen( Scaffold( bottomBar = { BottomAppBar(navController) } ) { innerPadding -> - NavigationGraph( - navController = navController, - modifier = Modifier.padding(innerPadding), - state = state, - onEvent = onEvent - ) + Box { + if (state.isLoading) { + CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) + } + NavigationGraph( + navController = navController, + modifier = Modifier.padding(innerPadding), + state = state, + onEvent = onEvent + ) + } } } diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/model/MainState.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/model/MainState.kt index 0cb866a..ba75158 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/model/MainState.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/model/MainState.kt @@ -5,5 +5,6 @@ import com.mani.quotify007.domain.model.Quote data class MainState( val quotes: List = emptyList(), val favoriteQuotes: List = emptyList(), - val randomQuote: Quote? = null + val randomQuote: Quote? = null, + var isLoading: Boolean = false ) \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt index 48305e8..e76772e 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt @@ -8,10 +8,12 @@ import androidx.lifecycle.viewModelScope import com.mani.quotify007.QuotifyApp import com.mani.quotify007.data.local.FavoriteQuoteEntity import com.mani.quotify007.domain.model.Quote +import com.mani.quotify007.domain.model.QuoteResult import com.mani.quotify007.ui.navigation.model.MainEvent import com.mani.quotify007.ui.navigation.model.MainState import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.launch class MainViewModel(private val application: Application) : AndroidViewModel(application) { @@ -29,22 +31,36 @@ class MainViewModel(private val application: Application) : AndroidViewModel(app private val getQuoteUseCase = (application as QuotifyApp).getQuoteUseCase + private lateinit var quoteResult: QuoteResult + init { + loadQuotesData() + } + + private fun loadQuotesData() { viewModelScope.launch { + _state.value.isLoading = true + try { + quoteResult = getQuoteUseCase.result() + } catch (e: Exception) { + _showToast.postValue(e.message) + } finally { + _state.value.isLoading = false + } (application as QuotifyApp).quoteDb.favoriteQuoteDao().getAllFavoriteQuotes() + .catch { e -> e.printStackTrace() } .collect { quotes -> _state.value = _state.value.copy(favoriteQuotes = quotes.map { it.toDomainModel() }) _state.value = _state.value.copy( - quotes = getQuoteUseCase.execute().map { quote -> - if (state.value.favoriteQuotes.any { it.id == quote.id }) { + quotes = quoteResult.results.map { quote -> + if (state.value.favoriteQuotes.any { it._id == quote._id }) { quote.isFavorite = true } else { quote.isFavorite = false } quote - }, - randomQuote = getQuoteUseCase.execute().randomOrNull() + } ) } } @@ -79,7 +95,7 @@ class MainViewModel(private val application: Application) : AndroidViewModel(app } } - private fun FavoriteQuoteEntity.toDomainModel() = Quote(id, text, author, true) + private fun FavoriteQuoteEntity.toDomainModel() = Quote(id, content = content, author = author, isFavorite = true) - private fun Quote.toEntity() = FavoriteQuoteEntity(id = id, text = text, author = author ?: "") + private fun Quote.toEntity() = FavoriteQuoteEntity(id = _id, content = content, author = author) } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/home/HomeScreen.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/home/HomeScreen.kt index 0392db8..92c739c 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/home/HomeScreen.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/home/HomeScreen.kt @@ -37,9 +37,10 @@ fun HomeScreen( horizontalAlignment = Alignment.CenterHorizontally ) { Text(QUOTE_OF_THE_DAY_HEADER, fontStyle = FontStyle.Italic, fontSize = 30.sp) + if (state.randomQuote == null) onEvent(MainEvent.GetRandomQuote) state.randomQuote?.let { quote -> QuotesScreen( - quote = quote, + quote = state.randomQuote, onEvent = onEvent, isAddOnly = true ) diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/quote/QuotesScreen.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/quote/QuotesScreen.kt index 9730016..83fd6c4 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/quote/QuotesScreen.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/quote/QuotesScreen.kt @@ -51,7 +51,7 @@ fun QuotesScreen( .padding(16.dp) ) { Text( - text = quote.text, + text = quote.content, style = MaterialTheme.typography.titleMedium, color = Color.Black, fontFamily = FontFamily.Serif, @@ -121,7 +121,7 @@ fun QuotesScreen( @Composable fun QuotesScreenPreview() { QuotesScreen( - Quote(0, "Sample quote", "Sample author"), + Quote(_id = "000", content = "Sample quote", author = "Sample author"), onEvent = {}, true ) diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/search/SearchScreen.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/search/SearchScreen.kt index 369a5ab..f29e3ac 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/search/SearchScreen.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/search/SearchScreen.kt @@ -36,7 +36,7 @@ fun SearchScreen( ) { var searchQuery by remember { mutableStateOf(TextFieldValue("")) } val filteredQuotes = - state.quotes.filter { it.text.contains(searchQuery.text, ignoreCase = true) } + state.quotes.filter { it.content.contains(searchQuery.text, ignoreCase = true) } Column(modifier = Modifier .fillMaxSize() diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ClipboardUtil.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ClipboardUtil.kt index d5fcc23..7078e5a 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ClipboardUtil.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ClipboardUtil.kt @@ -11,7 +11,7 @@ fun onCopyText(context: Context, quote: Quote) { clipboardManager.setPrimaryClip( ClipData.newPlainText( "quote", - "${quote.text} - ${quote.author}" + "${quote.content} - ${quote.author}" ) ) } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ShareUtil.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ShareUtil.kt index abcfba5..37f38f7 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ShareUtil.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ShareUtil.kt @@ -7,7 +7,7 @@ import com.mani.quotify007.domain.model.Quote fun shareQuote(context: Context, quote: Quote) { val shareIntent = Intent().apply { action = Intent.ACTION_SEND - putExtra(Intent.EXTRA_TEXT, "${quote.text} - ${quote.author}") + putExtra(Intent.EXTRA_TEXT, "${quote.content} - ${quote.author}") type = "text/plain" } context.startActivity(Intent.createChooser(shareIntent, null)) diff --git a/Mani-Quotify/app/src/main/res/raw/api_quotable_io.pem b/Mani-Quotify/app/src/main/res/raw/api_quotable_io.pem new file mode 100644 index 0000000..a4255df --- /dev/null +++ b/Mani-Quotify/app/src/main/res/raw/api_quotable_io.pem @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIE7TCCA9WgAwIBAgISA/ei+uPb3vM4UWafWlbTMdGzMA0GCSqGSIb3DQEBCwUA +MDMxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQwwCgYDVQQD +EwNSMTAwHhcNMjQwNjEyMjMwNTA2WhcNMjQwOTEwMjMwNTA1WjAaMRgwFgYDVQQD +Ew9hcGkucXVvdGFibGUuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQDSdLfJgZR0UGYFWvAczzox7BC9yrdicfeFKMs0usXefhudus77t7XToTFIaRz+ +HRbq0uGWaueoBg7TFHDu7G4atQ9Uu03GVvJE/G5u1GJyBKiaVya02oO13ll77S9S +2jMGlrn0Sy80DHLuG+7AjnrS1lyMQnPSYA0dK6rxV3QEHutJjLkYIbPmDoe71eEv +GjPA2m6ToVtxlIWHVHyaXMxc9Rw5w/tdZaod0TqtPs4hcpw5B9NXoG2qyZJjHBeo +dZl71SwcuKVi/F1TwKJpIkjhOHKq1byVBzUfJ83hTmDzVfvqnpuCvDLgndjzxvQw +KUTE1zWAxkik8v0APEN8riUZAgMBAAGjggISMIICDjAOBgNVHQ8BAf8EBAMCBaAw +HQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYD +VR0OBBYEFOYIf/xJHIiu70Hu33S6rc2K92hEMB8GA1UdIwQYMBaAFLu8w0el5Lyp +xsOkcgwQjaI14cjoMFcGCCsGAQUFBwEBBEswSTAiBggrBgEFBQcwAYYWaHR0cDov +L3IxMC5vLmxlbmNyLm9yZzAjBggrBgEFBQcwAoYXaHR0cDovL3IxMC5pLmxlbmNy +Lm9yZy8wGgYDVR0RBBMwEYIPYXBpLnF1b3RhYmxlLmlvMBMGA1UdIAQMMAowCAYG +Z4EMAQIBMIIBAwYKKwYBBAHWeQIEAgSB9ASB8QDvAHUAPxdLT9ciR1iUHWUchL4N +Eu2QN38fhWrrwb8ohez4ZG4AAAGQDumRgQAABAMARjBEAiAcrtm5Jz29I8ksTnE0 +XaF1SGvvSUdqVdoe87+LqIxmvAIgRpjfZ/jYYXYeXEps3SMmk25zU9rYauZsNYOu +THbAOXsAdgAZmBBxCfDWUi4wgNKeP2S7g24ozPkPUo7u385KPxa0ygAAAZAO6ZGW +AAAEAwBHMEUCIDDMovkuAnaB6bp9urfxSCIoTPGF5B3jp1C5Kwhi1rVNAiEA5bCt +Z2+EpQfVvXcnxaGTuT25uE/n8eVrHGVGyLTJm9kwDQYJKoZIhvcNAQELBQADggEB +AImQm9nT5D1b7zQmblhHaex284QM78Q+oS0/IPaRMc2uAA269fEQXtYlp8ubnZl3 +Mv3mOn/B8AsgQClTXALZ6x2pcRNCbluJP3RK7uFn/YRFkTG/az1sk3mE6uIf23mm +Zy7LCPbPmSR6hOKAwYoee+LrZsEcHT3J7eHk9pCCTbOFPzWg1hMptomyYt8omgm/ +m5iAQJNwUF3MUmmmu5/SSutNBL6PLLKa68KHgBuaYcSmzf1oBo3ioFMs1a34Yz9m +Kcj6ahMYxA3rpFDXj6RA4c08Q1H0/Up+x4/hUPZd+Z5HdEe1i9PJ6EDVNCcykqDb +cbFMb9/PpYSjg8XSZ0CrXnM= +-----END CERTIFICATE----- diff --git a/Mani-Quotify/gradle/libs.versions.toml b/Mani-Quotify/gradle/libs.versions.toml index 0ff70a9..f00f546 100644 --- a/Mani-Quotify/gradle/libs.versions.toml +++ b/Mani-Quotify/gradle/libs.versions.toml @@ -11,6 +11,8 @@ activityCompose = "1.9.2" composeBom = "2023.08.00" navigationCompose = "2.8.2" roomVersion = "2.6.1" +retrofit = "2.11.0" +converterGson = "2.11.0" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -24,6 +26,8 @@ androidx-ui-navigation = { group = "androidx.navigation", name = "navigation-com androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "roomVersion" } androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "roomVersion" } androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "roomVersion" } +converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" } +retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } androidx-ui = { group = "androidx.compose.ui", name = "ui" } androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } From 03d71581e4a0e2b096cc3e05e9233788fdb56031 Mon Sep 17 00:00:00 2001 From: itmani1990 Date: Wed, 9 Oct 2024 17:49:39 -0500 Subject: [PATCH 05/12] Features added -Removed GSON converter -Used Kotlin Serialization -Manual dependency items simplified --- Mani-Quotify/app/build.gradle.kts | 5 +- .../java/com/mani/quotify007/QuotifyApp.kt | 33 ++--------- .../com/mani/quotify007/QuotiyAppModule.kt | 55 ++++++++++++++++++ .../quotify007/data/remote/QuoteApiService.kt | 4 +- .../data/remote/mapper/QuoteMapper.kt | 21 +++++++ .../data/remote/model/QuoteNetworkModel.kt | 27 +++++++++ .../data/repository/QuoteRepositoryImpl.kt | 25 +++++--- .../data/repository/QuoteStaticData.kt | 12 ++-- .../com/mani/quotify007/domain/model/Quote.kt | 9 +-- .../quotify007/domain/model/QuoteResult.kt | 10 ++-- .../domain/repository/QuoteRepository.kt | 4 +- .../domain/usecase/GetQuoteUseCase.kt | 3 + .../com/mani/quotify007/ui/MainActivity.kt | 11 +++- .../ui/navigation/viewmodel/MainViewModel.kt | 57 +++++++------------ .../viewmodel/QuoteViewModelFactory.kt | 17 ++++++ .../ui/screens/quote/QuotesScreen.kt | 2 +- .../quotify007/ui/screens/utils/ApiUtil.kt | 3 + Mani-Quotify/gradle/libs.versions.toml | 11 +++- 18 files changed, 210 insertions(+), 99 deletions(-) create mode 100644 Mani-Quotify/app/src/main/java/com/mani/quotify007/QuotiyAppModule.kt create mode 100644 Mani-Quotify/app/src/main/java/com/mani/quotify007/data/remote/mapper/QuoteMapper.kt create mode 100644 Mani-Quotify/app/src/main/java/com/mani/quotify007/data/remote/model/QuoteNetworkModel.kt create mode 100644 Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/QuoteViewModelFactory.kt create mode 100644 Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ApiUtil.kt diff --git a/Mani-Quotify/app/build.gradle.kts b/Mani-Quotify/app/build.gradle.kts index ee1188d..3577b48 100644 --- a/Mani-Quotify/app/build.gradle.kts +++ b/Mani-Quotify/app/build.gradle.kts @@ -2,6 +2,7 @@ plugins { alias(libs.plugins.androidApplication) alias(libs.plugins.jetbrainsKotlinAndroid) alias(libs.plugins.ksp) + alias(libs.plugins.kotlinSerialization) } android { @@ -64,7 +65,9 @@ dependencies { implementation(libs.androidx.room.runtime) implementation(libs.androidx.room.ktx) implementation(libs.retrofit) - implementation(libs.converter.gson) + implementation(libs.ktx.sxn.json) + implementation(libs.ktx.sxn.converter) + implementation(libs.okhttp) ksp(libs.androidx.room.compiler) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/QuotifyApp.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/QuotifyApp.kt index 2d8565d..447e894 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/QuotifyApp.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/QuotifyApp.kt @@ -1,36 +1,15 @@ package com.mani.quotify007 import android.app.Application -import androidx.room.Room -import com.mani.quotify007.data.local.QuotifyDatabase -import com.mani.quotify007.data.remote.QuoteApiService -import com.mani.quotify007.data.remote.getSafeOkHttpClient -import com.mani.quotify007.data.repository.QuoteRepositoryImpl -import com.mani.quotify007.domain.usecase.GetQuoteUseCase -import com.mani.quotify007.ui.screens.utils.QUOTIFY_DB_NAME -import retrofit2.Retrofit -import retrofit2.converter.gson.GsonConverterFactory -private const val BASE_URL = "https://api.quotable.io/" class QuotifyApp: Application() { - lateinit var api: QuoteApiService - lateinit var quoteDb: QuotifyDatabase - lateinit var getQuoteUseCase: GetQuoteUseCase + companion object { + /* Hold a static reference to the AppModule instance, which can be accessed from anywhere + * in the application without needing an instance of the Application class */ + lateinit var instance: QuotiyAppModule + } override fun onCreate() { super.onCreate() - api = Retrofit.Builder() - .baseUrl(BASE_URL) - .client(getSafeOkHttpClient(this)) - .addConverterFactory(GsonConverterFactory.create()) - .build() - .create(QuoteApiService::class.java) - - quoteDb = Room.databaseBuilder( - applicationContext, - QuotifyDatabase::class.java, - QUOTIFY_DB_NAME - ).build() - val quoteRepository = QuoteRepositoryImpl(this) - getQuoteUseCase = GetQuoteUseCase(repository = quoteRepository) + instance = QuotiyAppModuleImpl(this) } } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/QuotiyAppModule.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/QuotiyAppModule.kt new file mode 100644 index 0000000..f100f23 --- /dev/null +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/QuotiyAppModule.kt @@ -0,0 +1,55 @@ +package com.mani.quotify007 + +import androidx.room.Room +import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory +import com.mani.quotify007.data.local.FavoriteQuoteDao +import com.mani.quotify007.data.local.QuotifyDatabase +import com.mani.quotify007.data.remote.QuoteApiService +import com.mani.quotify007.data.remote.getSafeOkHttpClient +import com.mani.quotify007.data.repository.QuoteRepositoryImpl +import com.mani.quotify007.domain.repository.QuoteRepository +import com.mani.quotify007.domain.usecase.GetQuoteUseCase +import com.mani.quotify007.ui.screens.utils.BASE_URL +import com.mani.quotify007.ui.screens.utils.QUOTIFY_DB_NAME +import kotlinx.serialization.json.Json +import okhttp3.MediaType.Companion.toMediaType +import retrofit2.Retrofit + +interface QuotiyAppModule { + val retrofit: Retrofit + val quoteDb: QuotifyDatabase + val quoteApiService: QuoteApiService + val quoteRepository: QuoteRepository + val quoteUseCase: GetQuoteUseCase + val quoteFavoriteQuoteDao: FavoriteQuoteDao +} + +class QuotiyAppModuleImpl(context: QuotifyApp) : QuotiyAppModule { + override val retrofit: Retrofit by lazy { + Retrofit.Builder() + .baseUrl(BASE_URL) + .client(getSafeOkHttpClient(context)) + .addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) + .build() + } + override val quoteDb: QuotifyDatabase by lazy { + Room.databaseBuilder( + context, + QuotifyDatabase::class.java, + QUOTIFY_DB_NAME + ).build() + } + + override val quoteApiService: QuoteApiService by lazy { + retrofit.create(QuoteApiService::class.java) + } + override val quoteRepository: QuoteRepository by lazy { + QuoteRepositoryImpl(quoteApiService, quoteDb) + } + override val quoteUseCase: GetQuoteUseCase by lazy { + GetQuoteUseCase(quoteRepository) + } + override val quoteFavoriteQuoteDao: FavoriteQuoteDao by lazy { + quoteDb.favoriteQuoteDao() + } +} \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/remote/QuoteApiService.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/remote/QuoteApiService.kt index 152ad92..e0bf93a 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/remote/QuoteApiService.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/remote/QuoteApiService.kt @@ -1,9 +1,9 @@ package com.mani.quotify007.data.remote -import com.mani.quotify007.domain.model.QuoteResult +import com.mani.quotify007.data.remote.model.QuoteNetworkModel import retrofit2.http.GET interface QuoteApiService { @GET("quotes") - suspend fun getQuoteResult(): QuoteResult + suspend fun getQuoteResult(): QuoteNetworkModel } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/remote/mapper/QuoteMapper.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/remote/mapper/QuoteMapper.kt new file mode 100644 index 0000000..ea0fa0e --- /dev/null +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/remote/mapper/QuoteMapper.kt @@ -0,0 +1,21 @@ +package com.mani.quotify007.data.remote.mapper + +import com.mani.quotify007.data.remote.model.QuoteNetworkModel +import com.mani.quotify007.domain.model.Quote +import com.mani.quotify007.domain.model.QuoteResult + +fun QuoteNetworkModel.toResults() = QuoteResult( + count = count, + lastItemIndex = lastItemIndex, + page = page, + results = results.map { + Quote( + id = it.id, + author = it.author, + content = it.content, + isFavorite = false + ) + }, + totalCount = totalCount, + totalPages = totalPages +) diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/remote/model/QuoteNetworkModel.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/remote/model/QuoteNetworkModel.kt new file mode 100644 index 0000000..58e34bc --- /dev/null +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/remote/model/QuoteNetworkModel.kt @@ -0,0 +1,27 @@ +package com.mani.quotify007.data.remote.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class QuoteNetworkModel ( + val count: Long, + val totalCount: Long, + val page: Long, + val totalPages: Long, + val lastItemIndex: Long, + val results: List +) + +@Serializable +data class QuoteItemNetworkModel ( + @SerialName("_id") + val id: String, + val author: String, + val content: String, + val tags: List, + val authorSlug: String, + val length: Long, + val dateAdded: String, + val dateModified: String +) \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteRepositoryImpl.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteRepositoryImpl.kt index 4d2edd6..0998554 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteRepositoryImpl.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteRepositoryImpl.kt @@ -1,16 +1,22 @@ package com.mani.quotify007.data.repository -import com.mani.quotify007.QuotifyApp import com.mani.quotify007.data.local.FavoriteQuoteEntity +import com.mani.quotify007.data.local.QuotifyDatabase +import com.mani.quotify007.data.remote.QuoteApiService +import com.mani.quotify007.data.remote.mapper.toResults import com.mani.quotify007.domain.model.Quote import com.mani.quotify007.domain.model.QuoteResult import com.mani.quotify007.domain.repository.QuoteRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -class QuoteRepositoryImpl(private val application: QuotifyApp) : QuoteRepository { +class QuoteRepositoryImpl( + private val quoteApiService: QuoteApiService, + private val quoteDb: QuotifyDatabase +) : QuoteRepository { - override suspend fun getQuoteResult(): QuoteResult = application.api.getQuoteResult() + override suspend fun getQuoteResult(): QuoteResult = + quoteApiService.getQuoteResult().toResults() override fun getQuotes(): List = quotesDataList @@ -24,20 +30,21 @@ class QuoteRepositoryImpl(private val application: QuotifyApp) : QuoteRepository quotesDataList.remove(quote) } - override fun getFavoriteQuotes(): Flow> = - application.quoteDb.favoriteQuoteDao().getAllFavoriteQuotes().map {entities -> + override suspend fun getFavoriteQuotes(): Flow> = + quoteDb.favoriteQuoteDao().getAllFavoriteQuotes().map {entities -> entities.map { entity -> entity.toDomainModel() } } override suspend fun addFavoriteQuote(quote: Quote) { - application.quoteDb.favoriteQuoteDao().insertFavoriteQuote(quote.toEntity()) + quoteDb.favoriteQuoteDao().insertFavoriteQuote(quote.toEntity()) } override suspend fun removeFavoriteQuote(quote: Quote) { - application.quoteDb.favoriteQuoteDao().deleteFavoriteQuote(quote.toEntity()) + quoteDb.favoriteQuoteDao().deleteFavoriteQuote(quote.toEntity()) } } -private fun Quote.toEntity() = FavoriteQuoteEntity(id = _id, content = content, author = author) +private fun Quote.toEntity() = FavoriteQuoteEntity(id = id, content = content, author = author) -private fun FavoriteQuoteEntity.toDomainModel() = Quote(id, content = content, author = author) +private fun FavoriteQuoteEntity.toDomainModel() = + Quote(id, content = content, author = author, isFavorite = true) diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteStaticData.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteStaticData.kt index 208641c..2457529 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteStaticData.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteStaticData.kt @@ -4,34 +4,34 @@ import com.mani.quotify007.domain.model.Quote val quotesDataList = mutableListOf( Quote( - _id = "111", + id = "111", content = "The greatest glory in living lies not in never falling, " + "but in rising every time we fall.", author = "Nelson Mandela" ), Quote( - _id = "222", + id = "222", content = "The way to get started is to quit talking and begin doing.", author = "Walt Disney" ), Quote( - _id = "333", + id = "333", content = "Your time is limited, so don't waste it living someone else's life.", author = "Steve Jobs" ), Quote( - _id = "444", + id = "444", content = "If life were predictable it would cease to be life, and be without flavor.", author = "Eleanor Roosevelt" ), Quote( - _id = "555", + id = "555", content = "If you look at what you have in life, you'll always have more. " + "If you look at what you don't have in life, you'll never have enough.", author = "Oprah Winfrey" ), Quote( - _id = "666", + id = "666", content = "If you set your goals ridiculously high and it's a failure, " + "you will fail above everyone else's success.", author = "James Cameron" diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/model/Quote.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/model/Quote.kt index 9fca7d1..f2c95b8 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/model/Quote.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/model/Quote.kt @@ -1,13 +1,8 @@ package com.mani.quotify007.domain.model data class Quote( - val _id: String, - val author: String, - val authorSlug: String? = null, + val id: String, val content: String, - val dateAdded: String? = null, - val dateModified: String? = null, - val length: Int? = null, - val tags: List? = null, + val author: String, var isFavorite: Boolean = false ) diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/model/QuoteResult.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/model/QuoteResult.kt index d118ac8..3ace6da 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/model/QuoteResult.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/model/QuoteResult.kt @@ -1,10 +1,10 @@ package com.mani.quotify007.domain.model data class QuoteResult( - val count: Int, - val lastItemIndex: Int, - val page: Int, + val count: Long, + val lastItemIndex: Long, + val page: Long, val results: List, - val totalCount: Int, - val totalPages: Int + val totalCount: Long, + val totalPages: Long ) \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/repository/QuoteRepository.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/repository/QuoteRepository.kt index fbcf3c9..5cb4832 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/repository/QuoteRepository.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/repository/QuoteRepository.kt @@ -4,12 +4,12 @@ import com.mani.quotify007.domain.model.Quote import com.mani.quotify007.domain.model.QuoteResult import kotlinx.coroutines.flow.Flow -interface QuoteRepository { + interface QuoteRepository { suspend fun getQuoteResult(): QuoteResult fun getQuotes(): List fun addQuote(quote: Quote) fun removeQuote(quote: Quote) - fun getFavoriteQuotes(): Flow> + suspend fun getFavoriteQuotes(): Flow> suspend fun addFavoriteQuote(quote: Quote) suspend fun removeFavoriteQuote(quote: Quote) } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/usecase/GetQuoteUseCase.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/usecase/GetQuoteUseCase.kt index 2d59458..249ec14 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/usecase/GetQuoteUseCase.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/usecase/GetQuoteUseCase.kt @@ -6,6 +6,9 @@ import com.mani.quotify007.domain.repository.QuoteRepository class GetQuoteUseCase(private val repository: QuoteRepository) { suspend fun result(): QuoteResult = repository.getQuoteResult() + suspend fun dbQuotes() = repository.getFavoriteQuotes() + suspend fun addFavoriteQuote(quote: Quote) = repository.addFavoriteQuote(quote) + suspend fun removeFavoriteQuote(quote: Quote) = repository.removeFavoriteQuote(quote) // TODO: Use when static quote data used fun execute(): List = repository.getQuotes() } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/MainActivity.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/MainActivity.kt index 4d739fc..2d8e6cc 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/MainActivity.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/MainActivity.kt @@ -3,10 +3,12 @@ package com.mani.quotify007.ui import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.activity.viewModels import androidx.compose.runtime.collectAsState +import androidx.lifecycle.ViewModelProvider +import com.mani.quotify007.QuotifyApp import com.mani.quotify007.ui.navigation.MainScreen import com.mani.quotify007.ui.navigation.viewmodel.MainViewModel +import com.mani.quotify007.ui.navigation.viewmodel.QuoteViewModelFactory import com.mani.quotify007.ui.screens.utils.onCopyText import com.mani.quotify007.ui.screens.utils.shareQuote import com.mani.quotify007.ui.screens.utils.showToast @@ -14,7 +16,12 @@ import com.mani.quotify007.ui.theme.QuotifyAppTheme class MainActivity : ComponentActivity() { - private val viewModel: MainViewModel by viewModels() + private val viewModel: MainViewModel by lazy { + ViewModelProvider( + this, + factory = QuoteViewModelFactory(QuotifyApp.instance.quoteUseCase) + ).get(MainViewModel::class.java) + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt index e76772e..34bd736 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt @@ -1,14 +1,11 @@ package com.mani.quotify007.ui.navigation.viewmodel -import android.app.Application -import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.mani.quotify007.QuotifyApp -import com.mani.quotify007.data.local.FavoriteQuoteEntity import com.mani.quotify007.domain.model.Quote -import com.mani.quotify007.domain.model.QuoteResult +import com.mani.quotify007.domain.usecase.GetQuoteUseCase import com.mani.quotify007.ui.navigation.model.MainEvent import com.mani.quotify007.ui.navigation.model.MainState import kotlinx.coroutines.flow.MutableStateFlow @@ -16,7 +13,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.launch -class MainViewModel(private val application: Application) : AndroidViewModel(application) { +class MainViewModel(private val useCase: GetQuoteUseCase) : ViewModel() { private val _state = MutableStateFlow(MainState()) val state: StateFlow = _state @@ -29,10 +26,6 @@ class MainViewModel(private val application: Application) : AndroidViewModel(app private val _showToast = MutableLiveData() val showToast: LiveData = _showToast - private val getQuoteUseCase = (application as QuotifyApp).getQuoteUseCase - - private lateinit var quoteResult: QuoteResult - init { loadQuotesData() } @@ -41,28 +34,28 @@ class MainViewModel(private val application: Application) : AndroidViewModel(app viewModelScope.launch { _state.value.isLoading = true try { - quoteResult = getQuoteUseCase.result() + useCase.dbQuotes() + .catch { e -> e.printStackTrace() } + .collect { quotes -> + _state.value = + _state.value.copy(favoriteQuotes = quotes) + _state.value = _state.value.copy( + quotes = useCase.result().results.map { quote -> + if (state.value.favoriteQuotes.any { it.id == quote.id }) { + quote.isFavorite = true + } else { + quote.isFavorite = false + } + quote + } + ) + _state.value.isLoading = false + } } catch (e: Exception) { _showToast.postValue(e.message) } finally { _state.value.isLoading = false } - (application as QuotifyApp).quoteDb.favoriteQuoteDao().getAllFavoriteQuotes() - .catch { e -> e.printStackTrace() } - .collect { quotes -> - _state.value = - _state.value.copy(favoriteQuotes = quotes.map { it.toDomainModel() }) - _state.value = _state.value.copy( - quotes = quoteResult.results.map { quote -> - if (state.value.favoriteQuotes.any { it._id == quote._id }) { - quote.isFavorite = true - } else { - quote.isFavorite = false - } - quote - } - ) - } } } @@ -70,15 +63,13 @@ class MainViewModel(private val application: Application) : AndroidViewModel(app viewModelScope.launch { when (event) { is MainEvent.AddFavorite -> { - (application as QuotifyApp).quoteDb.favoriteQuoteDao() - .insertFavoriteQuote(event.quote.toEntity()) + useCase.addFavoriteQuote(event.quote) _state.value = _state.value.copy( favoriteQuotes = _state.value.favoriteQuotes + event.quote ) } is MainEvent.RemoveFavorite -> { - (application as QuotifyApp).quoteDb.favoriteQuoteDao() - .deleteFavoriteQuote(event.quote.toEntity()) + useCase.removeFavoriteQuote(event.quote) _state.value = _state.value.copy( favoriteQuotes = _state.value.favoriteQuotes - event.quote ) @@ -94,8 +85,4 @@ class MainViewModel(private val application: Application) : AndroidViewModel(app } } } - - private fun FavoriteQuoteEntity.toDomainModel() = Quote(id, content = content, author = author, isFavorite = true) - - private fun Quote.toEntity() = FavoriteQuoteEntity(id = _id, content = content, author = author) } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/QuoteViewModelFactory.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/QuoteViewModelFactory.kt new file mode 100644 index 0000000..57e7fef --- /dev/null +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/QuoteViewModelFactory.kt @@ -0,0 +1,17 @@ +package com.mani.quotify007.ui.navigation.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.mani.quotify007.domain.usecase.GetQuoteUseCase + +class QuoteViewModelFactory( + private val useCase: GetQuoteUseCase +): ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(MainViewModel::class.java)) { + @Suppress("UNCHECKED_CAST") + return MainViewModel(useCase) as T + } + throw IllegalArgumentException("Unknown ViewModel class") + } +} \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/quote/QuotesScreen.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/quote/QuotesScreen.kt index 83fd6c4..7b93607 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/quote/QuotesScreen.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/quote/QuotesScreen.kt @@ -121,7 +121,7 @@ fun QuotesScreen( @Composable fun QuotesScreenPreview() { QuotesScreen( - Quote(_id = "000", content = "Sample quote", author = "Sample author"), + Quote(id = "000", content = "Sample quote", author = "Sample author"), onEvent = {}, true ) diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ApiUtil.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ApiUtil.kt new file mode 100644 index 0000000..bd8b180 --- /dev/null +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ApiUtil.kt @@ -0,0 +1,3 @@ +package com.mani.quotify007.ui.screens.utils + +const val BASE_URL = "https://api.quotable.io/" \ No newline at end of file diff --git a/Mani-Quotify/gradle/libs.versions.toml b/Mani-Quotify/gradle/libs.versions.toml index f00f546..0ac2606 100644 --- a/Mani-Quotify/gradle/libs.versions.toml +++ b/Mani-Quotify/gradle/libs.versions.toml @@ -2,6 +2,7 @@ agp = "8.3.0" kotlin = "1.9.0" ksp = "1.9.0-1.0.13" +serializationPlugin = "1.9.22" coreKtx = "1.13.1" junit = "4.13.2" junitVersion = "1.2.1" @@ -12,7 +13,9 @@ composeBom = "2023.08.00" navigationCompose = "2.8.2" roomVersion = "2.6.1" retrofit = "2.11.0" -converterGson = "2.11.0" +jsonConverter = "1.6.0" +ktxConvertor = "1.0.0" +okhttp = "4.11.0" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -26,7 +29,10 @@ androidx-ui-navigation = { group = "androidx.navigation", name = "navigation-com androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "roomVersion" } androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "roomVersion" } androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "roomVersion" } -converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" } +# Kotlin serialization +ktx-sxn-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "jsonConverter" } +ktx-sxn-converter = { module = "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter", version.ref = "ktxConvertor" } +okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } androidx-ui = { group = "androidx.compose.ui", name = "ui" } androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } @@ -40,4 +46,5 @@ androidx-material3 = { group = "androidx.compose.material3", name = "material3" androidApplication = { id = "com.android.application", version.ref = "agp" } jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } +kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "serializationPlugin" } From 627d6ec55aa1d3d712d18000fdbde59cf62dbf38 Mon Sep 17 00:00:00 2001 From: itmani1990 Date: Wed, 9 Oct 2024 17:53:47 -0500 Subject: [PATCH 06/12] Cleanup - Removed static data list --- .../data/repository/QuoteRepositoryImpl.kt | 8 +--- .../data/repository/QuoteStaticData.kt | 39 ------------------- .../domain/repository/QuoteRepository.kt | 1 - .../domain/usecase/GetQuoteUseCase.kt | 2 - 4 files changed, 2 insertions(+), 48 deletions(-) delete mode 100644 Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteStaticData.kt diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteRepositoryImpl.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteRepositoryImpl.kt index 0998554..4031680 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteRepositoryImpl.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteRepositoryImpl.kt @@ -18,16 +18,12 @@ class QuoteRepositoryImpl( override suspend fun getQuoteResult(): QuoteResult = quoteApiService.getQuoteResult().toResults() - override fun getQuotes(): List = quotesDataList - - //TODO: Future implementation override fun addQuote(quote: Quote) { - quotesDataList.add(quote) + //TODO: add quote to list } - //TODO: Future implementation override fun removeQuote(quote: Quote) { - quotesDataList.remove(quote) + //TODO: remove quote from list } override suspend fun getFavoriteQuotes(): Flow> = diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteStaticData.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteStaticData.kt deleted file mode 100644 index 2457529..0000000 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/data/repository/QuoteStaticData.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.mani.quotify007.data.repository - -import com.mani.quotify007.domain.model.Quote - -val quotesDataList = mutableListOf( - Quote( - id = "111", - content = "The greatest glory in living lies not in never falling, " + - "but in rising every time we fall.", - author = "Nelson Mandela" - ), - Quote( - id = "222", - content = "The way to get started is to quit talking and begin doing.", - author = "Walt Disney" - ), - Quote( - id = "333", - content = "Your time is limited, so don't waste it living someone else's life.", - author = "Steve Jobs" - ), - Quote( - id = "444", - content = "If life were predictable it would cease to be life, and be without flavor.", - author = "Eleanor Roosevelt" - ), - Quote( - id = "555", - content = "If you look at what you have in life, you'll always have more. " + - "If you look at what you don't have in life, you'll never have enough.", - author = "Oprah Winfrey" - ), - Quote( - id = "666", - content = "If you set your goals ridiculously high and it's a failure, " + - "you will fail above everyone else's success.", - author = "James Cameron" - ) -) \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/repository/QuoteRepository.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/repository/QuoteRepository.kt index 5cb4832..23bcd5e 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/repository/QuoteRepository.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/repository/QuoteRepository.kt @@ -6,7 +6,6 @@ import kotlinx.coroutines.flow.Flow interface QuoteRepository { suspend fun getQuoteResult(): QuoteResult - fun getQuotes(): List fun addQuote(quote: Quote) fun removeQuote(quote: Quote) suspend fun getFavoriteQuotes(): Flow> diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/usecase/GetQuoteUseCase.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/usecase/GetQuoteUseCase.kt index 249ec14..cd75a1b 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/usecase/GetQuoteUseCase.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/usecase/GetQuoteUseCase.kt @@ -9,6 +9,4 @@ class GetQuoteUseCase(private val repository: QuoteRepository) { suspend fun dbQuotes() = repository.getFavoriteQuotes() suspend fun addFavoriteQuote(quote: Quote) = repository.addFavoriteQuote(quote) suspend fun removeFavoriteQuote(quote: Quote) = repository.removeFavoriteQuote(quote) - // TODO: Use when static quote data used - fun execute(): List = repository.getQuotes() } \ No newline at end of file From 8658fcce774d9a6e00abf921eeb3a52bb0739c49 Mon Sep 17 00:00:00 2001 From: itmani1990 Date: Wed, 9 Oct 2024 18:34:26 -0500 Subject: [PATCH 07/12] Cleanup - Handled the repetative livedata approach into sharedflow with Event --- .../com/mani/quotify007/ui/MainActivity.kt | 24 ++++++++++++------ .../ui/navigation/model/UiActionEvent.kt | 9 +++++++ .../ui/navigation/viewmodel/MainViewModel.kt | 25 ++++++++----------- 3 files changed, 35 insertions(+), 23 deletions(-) create mode 100644 Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/model/UiActionEvent.kt diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/MainActivity.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/MainActivity.kt index 2d8e6cc..1dd5146 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/MainActivity.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/MainActivity.kt @@ -4,15 +4,20 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.runtime.collectAsState +import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import com.mani.quotify007.QuotifyApp import com.mani.quotify007.ui.navigation.MainScreen +import com.mani.quotify007.ui.navigation.model.UiActionEvent import com.mani.quotify007.ui.navigation.viewmodel.MainViewModel import com.mani.quotify007.ui.navigation.viewmodel.QuoteViewModelFactory import com.mani.quotify007.ui.screens.utils.onCopyText import com.mani.quotify007.ui.screens.utils.shareQuote import com.mani.quotify007.ui.screens.utils.showToast import com.mani.quotify007.ui.theme.QuotifyAppTheme +import kotlinx.coroutines.launch class MainActivity : ComponentActivity() { @@ -35,14 +40,17 @@ class MainActivity : ComponentActivity() { ) } } - viewModel.copyTextEvent.observe(this) { quote -> - onCopyText(this, quote) - } - viewModel.shareClickEvent.observe(this) { quote -> - shareQuote(this, quote) - } - viewModel.showToast.observe(this) { message -> - message?.let { showToast(it) } + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.uiActionEvent.collect { event -> + when (event) { + is UiActionEvent.CopyText -> onCopyText(this@MainActivity, event.quote) + is UiActionEvent.ShareClick -> shareQuote(this@MainActivity, event.quote) + is UiActionEvent.ShowToast -> showToast(event.message) + else -> { /* Do nothing */} + } + } + } } } } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/model/UiActionEvent.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/model/UiActionEvent.kt new file mode 100644 index 0000000..865eba4 --- /dev/null +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/model/UiActionEvent.kt @@ -0,0 +1,9 @@ +package com.mani.quotify007.ui.navigation.model + +import com.mani.quotify007.domain.model.Quote + +sealed class UiActionEvent { + data class CopyText(val quote: Quote) : UiActionEvent() + data class ShareClick(val quote: Quote) : UiActionEvent() + data class ShowToast(val message: String) : UiActionEvent() +} \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt index 34bd736..2d56b8a 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt @@ -1,14 +1,14 @@ package com.mani.quotify007.ui.navigation.viewmodel -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.mani.quotify007.domain.model.Quote import com.mani.quotify007.domain.usecase.GetQuoteUseCase import com.mani.quotify007.ui.navigation.model.MainEvent import com.mani.quotify007.ui.navigation.model.MainState +import com.mani.quotify007.ui.navigation.model.UiActionEvent +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.launch @@ -17,14 +17,8 @@ class MainViewModel(private val useCase: GetQuoteUseCase) : ViewModel() { private val _state = MutableStateFlow(MainState()) val state: StateFlow = _state - private val _copyTextEvent = MutableLiveData() - val copyTextEvent: LiveData = _copyTextEvent - - private val _shareClickEvent = MutableLiveData() - val shareClickEvent: LiveData = _shareClickEvent - - private val _showToast = MutableLiveData() - val showToast: LiveData = _showToast + private val _uiActionEvent = MutableSharedFlow() + val uiActionEvent: SharedFlow = _uiActionEvent init { loadQuotesData() @@ -52,7 +46,7 @@ class MainViewModel(private val useCase: GetQuoteUseCase) : ViewModel() { _state.value.isLoading = false } } catch (e: Exception) { - _showToast.postValue(e.message) + _uiActionEvent.emit(UiActionEvent.ShowToast(e.message.toString())) } finally { _state.value.isLoading = false } @@ -78,10 +72,11 @@ class MainViewModel(private val useCase: GetQuoteUseCase) : ViewModel() { val randomQuote = _state.value.quotes.randomOrNull() _state.value = _state.value.copy(randomQuote = randomQuote) } + is MainEvent.CopyText -> _uiActionEvent.emit(UiActionEvent.CopyText(event.quote)) + + is MainEvent.ShareClick -> _uiActionEvent.emit(UiActionEvent.ShareClick(event.quote)) - is MainEvent.CopyText -> _copyTextEvent.postValue(event.quote) - is MainEvent.ShareClick -> _shareClickEvent.postValue(event.quote) - is MainEvent.ShowToast -> _showToast.postValue(event.message) + is MainEvent.ShowToast -> _uiActionEvent.emit(UiActionEvent.ShowToast(event.message)) } } } From 721faa2f688a8e2ae3447ad9c390b515e77bb488 Mon Sep 17 00:00:00 2001 From: itmani1990 Date: Wed, 9 Oct 2024 19:59:11 -0500 Subject: [PATCH 08/12] Feature added - Added Okhttp loggin interceptor for logs of HTTP requests and responses --- Mani-Quotify/app/build.gradle.kts | 1 + .../main/java/com/mani/quotify007/QuotiyAppModule.kt | 10 ++++++++-- .../com/mani/quotify007/ui/screens/utils/ApiUtil.kt | 3 ++- Mani-Quotify/gradle/libs.versions.toml | 1 + 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Mani-Quotify/app/build.gradle.kts b/Mani-Quotify/app/build.gradle.kts index 3577b48..d582f3c 100644 --- a/Mani-Quotify/app/build.gradle.kts +++ b/Mani-Quotify/app/build.gradle.kts @@ -68,6 +68,7 @@ dependencies { implementation(libs.ktx.sxn.json) implementation(libs.ktx.sxn.converter) implementation(libs.okhttp) + implementation(libs.okhttp.logging) ksp(libs.androidx.room.compiler) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/QuotiyAppModule.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/QuotiyAppModule.kt index f100f23..191df0b 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/QuotiyAppModule.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/QuotiyAppModule.kt @@ -10,9 +10,11 @@ import com.mani.quotify007.data.repository.QuoteRepositoryImpl import com.mani.quotify007.domain.repository.QuoteRepository import com.mani.quotify007.domain.usecase.GetQuoteUseCase import com.mani.quotify007.ui.screens.utils.BASE_URL +import com.mani.quotify007.ui.screens.utils.JSON_TYPE import com.mani.quotify007.ui.screens.utils.QUOTIFY_DB_NAME import kotlinx.serialization.json.Json import okhttp3.MediaType.Companion.toMediaType +import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit interface QuotiyAppModule { @@ -28,8 +30,8 @@ class QuotiyAppModuleImpl(context: QuotifyApp) : QuotiyAppModule { override val retrofit: Retrofit by lazy { Retrofit.Builder() .baseUrl(BASE_URL) - .client(getSafeOkHttpClient(context)) - .addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) + .client(getSafeOkHttpClient(context).newBuilder().addInterceptor(logging).build()) + .addConverterFactory(Json.asConverterFactory(JSON_TYPE.toMediaType())) .build() } override val quoteDb: QuotifyDatabase by lazy { @@ -52,4 +54,8 @@ class QuotiyAppModuleImpl(context: QuotifyApp) : QuotiyAppModule { override val quoteFavoriteQuoteDao: FavoriteQuoteDao by lazy { quoteDb.favoriteQuoteDao() } +} + +val logging = HttpLoggingInterceptor().apply { + level = HttpLoggingInterceptor.Level.BODY } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ApiUtil.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ApiUtil.kt index bd8b180..4d7ec62 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ApiUtil.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/screens/utils/ApiUtil.kt @@ -1,3 +1,4 @@ package com.mani.quotify007.ui.screens.utils -const val BASE_URL = "https://api.quotable.io/" \ No newline at end of file +const val BASE_URL = "https://api.quotable.io/" +const val JSON_TYPE = "application/json" \ No newline at end of file diff --git a/Mani-Quotify/gradle/libs.versions.toml b/Mani-Quotify/gradle/libs.versions.toml index 0ac2606..29f312e 100644 --- a/Mani-Quotify/gradle/libs.versions.toml +++ b/Mani-Quotify/gradle/libs.versions.toml @@ -33,6 +33,7 @@ androidx-room-compiler = { group = "androidx.room", name = "room-compiler", vers ktx-sxn-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "jsonConverter" } ktx-sxn-converter = { module = "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter", version.ref = "ktxConvertor" } okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } +okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" } retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } androidx-ui = { group = "androidx.compose.ui", name = "ui" } androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } From da5aa1651b1f3af0dd3ea567ec13ec37454beb1b Mon Sep 17 00:00:00 2001 From: itmani1990 Date: Wed, 9 Oct 2024 22:22:50 -0500 Subject: [PATCH 09/12] comment addressed --- .../quotify007/ui/navigation/viewmodel/MainViewModel.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt index 2d56b8a..9a0db41 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt @@ -31,9 +31,8 @@ class MainViewModel(private val useCase: GetQuoteUseCase) : ViewModel() { useCase.dbQuotes() .catch { e -> e.printStackTrace() } .collect { quotes -> - _state.value = - _state.value.copy(favoriteQuotes = quotes) _state.value = _state.value.copy( + favoriteQuotes = quotes, quotes = useCase.result().results.map { quote -> if (state.value.favoriteQuotes.any { it.id == quote.id }) { quote.isFavorite = true @@ -41,9 +40,9 @@ class MainViewModel(private val useCase: GetQuoteUseCase) : ViewModel() { quote.isFavorite = false } quote - } + }, + isLoading = false ) - _state.value.isLoading = false } } catch (e: Exception) { _uiActionEvent.emit(UiActionEvent.ShowToast(e.message.toString())) From 67800aa944660f43ef0f0a1bafe34e940e4c2e50 Mon Sep 17 00:00:00 2001 From: itmani1990 Date: Thu, 10 Oct 2024 20:15:22 -0500 Subject: [PATCH 10/12] Response handled with Success and failure state --- .../domain/repository/QuoteRepository.kt | 2 +- .../domain/usecase/GetQuoteUseCase.kt | 9 +++- .../ui/navigation/model/ResponseResult.kt | 6 +++ .../ui/navigation/viewmodel/MainViewModel.kt | 46 ++++++++++--------- 4 files changed, 40 insertions(+), 23 deletions(-) create mode 100644 Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/model/ResponseResult.kt diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/repository/QuoteRepository.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/repository/QuoteRepository.kt index 23bcd5e..fb3da42 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/repository/QuoteRepository.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/repository/QuoteRepository.kt @@ -4,7 +4,7 @@ import com.mani.quotify007.domain.model.Quote import com.mani.quotify007.domain.model.QuoteResult import kotlinx.coroutines.flow.Flow - interface QuoteRepository { +interface QuoteRepository { suspend fun getQuoteResult(): QuoteResult fun addQuote(quote: Quote) fun removeQuote(quote: Quote) diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/usecase/GetQuoteUseCase.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/usecase/GetQuoteUseCase.kt index cd75a1b..6cad5fe 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/usecase/GetQuoteUseCase.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/usecase/GetQuoteUseCase.kt @@ -3,9 +3,16 @@ package com.mani.quotify007.domain.usecase import com.mani.quotify007.domain.model.Quote import com.mani.quotify007.domain.model.QuoteResult import com.mani.quotify007.domain.repository.QuoteRepository +import com.mani.quotify007.ui.navigation.model.ResponseResult class GetQuoteUseCase(private val repository: QuoteRepository) { - suspend fun result(): QuoteResult = repository.getQuoteResult() + suspend fun result(): ResponseResult { + return try { + ResponseResult.Success(repository.getQuoteResult()) + } catch (e: Exception) { + ResponseResult.Error(e) + } + } suspend fun dbQuotes() = repository.getFavoriteQuotes() suspend fun addFavoriteQuote(quote: Quote) = repository.addFavoriteQuote(quote) suspend fun removeFavoriteQuote(quote: Quote) = repository.removeFavoriteQuote(quote) diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/model/ResponseResult.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/model/ResponseResult.kt new file mode 100644 index 0000000..2fcd398 --- /dev/null +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/model/ResponseResult.kt @@ -0,0 +1,6 @@ +package com.mani.quotify007.ui.navigation.model + +sealed class ResponseResult { + data class Success(val data: T) : ResponseResult() + data class Error(val exception: Exception) : ResponseResult() +} \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt index 9a0db41..06d29b9 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope import com.mani.quotify007.domain.usecase.GetQuoteUseCase import com.mani.quotify007.ui.navigation.model.MainEvent import com.mani.quotify007.ui.navigation.model.MainState +import com.mani.quotify007.ui.navigation.model.ResponseResult import com.mani.quotify007.ui.navigation.model.UiActionEvent import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -27,28 +28,31 @@ class MainViewModel(private val useCase: GetQuoteUseCase) : ViewModel() { private fun loadQuotesData() { viewModelScope.launch { _state.value.isLoading = true - try { - useCase.dbQuotes() - .catch { e -> e.printStackTrace() } - .collect { quotes -> - _state.value = _state.value.copy( - favoriteQuotes = quotes, - quotes = useCase.result().results.map { quote -> - if (state.value.favoriteQuotes.any { it.id == quote.id }) { - quote.isFavorite = true - } else { - quote.isFavorite = false - } - quote - }, - isLoading = false - ) + useCase.dbQuotes() + .catch { e -> e.printStackTrace() } + .collect { quotes -> + when(val result = useCase.result()) { + is ResponseResult.Success -> { + _state.value = _state.value.copy( + favoriteQuotes = quotes, + quotes = result.data.results.map { quote -> + quote.isFavorite = quotes.any { it.id == quote.id } + quote + }, + isLoading = false + ) + } + is ResponseResult.Error -> { + _state.value = _state.value.copy( + favoriteQuotes = quotes, + isLoading = false + ) + _uiActionEvent.emit( + UiActionEvent.ShowToast(result.exception.message.toString()) + ) + } } - } catch (e: Exception) { - _uiActionEvent.emit(UiActionEvent.ShowToast(e.message.toString())) - } finally { - _state.value.isLoading = false - } + } } } From 2a8e134b101f8d3508839a4c31f12f7e5ebea92d Mon Sep 17 00:00:00 2001 From: itmani1990 Date: Sat, 12 Oct 2024 13:54:42 -0500 Subject: [PATCH 11/12] Unittest for Kotlin Coroutines with MockK library --- Mani-Quotify/app/build.gradle.kts | 3 + .../quotify007/domain/model/QuoteResult.kt | 10 +-- .../ui/navigation/viewmodel/MainViewModel.kt | 2 +- .../navigation/viewmodel/MainViewModelTest.kt | 69 +++++++++++++++++++ Mani-Quotify/gradle/libs.versions.toml | 6 ++ 5 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 Mani-Quotify/app/src/test/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModelTest.kt diff --git a/Mani-Quotify/app/build.gradle.kts b/Mani-Quotify/app/build.gradle.kts index d582f3c..49688a5 100644 --- a/Mani-Quotify/app/build.gradle.kts +++ b/Mani-Quotify/app/build.gradle.kts @@ -77,4 +77,7 @@ dependencies { androidTestImplementation(libs.androidx.ui.test.junit4) debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) + testImplementation(libs.kotlinx.coroutines.test) + testImplementation(libs.mockk) + testImplementation(libs.kotlin.test.junit) } \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/model/QuoteResult.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/model/QuoteResult.kt index 3ace6da..0af1a08 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/model/QuoteResult.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/domain/model/QuoteResult.kt @@ -1,10 +1,10 @@ package com.mani.quotify007.domain.model data class QuoteResult( - val count: Long, - val lastItemIndex: Long, - val page: Long, + val count: Long? = null, + val lastItemIndex: Long? = null, + val page: Long? = null, val results: List, - val totalCount: Long, - val totalPages: Long + val totalCount: Long? = null, + val totalPages: Long? = null ) \ No newline at end of file diff --git a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt index 06d29b9..0f3db26 100644 --- a/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt +++ b/Mani-Quotify/app/src/main/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModel.kt @@ -25,7 +25,7 @@ class MainViewModel(private val useCase: GetQuoteUseCase) : ViewModel() { loadQuotesData() } - private fun loadQuotesData() { + fun loadQuotesData() { viewModelScope.launch { _state.value.isLoading = true useCase.dbQuotes() diff --git a/Mani-Quotify/app/src/test/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModelTest.kt b/Mani-Quotify/app/src/test/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModelTest.kt new file mode 100644 index 0000000..8e88652 --- /dev/null +++ b/Mani-Quotify/app/src/test/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModelTest.kt @@ -0,0 +1,69 @@ +package com.mani.quotify007.ui.navigation.viewmodel + +import com.mani.quotify007.domain.model.Quote +import com.mani.quotify007.domain.model.QuoteResult +import com.mani.quotify007.domain.usecase.GetQuoteUseCase +import com.mani.quotify007.ui.navigation.model.ResponseResult +import io.mockk.coEvery +import io.mockk.mockk +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.test.setMain +import org.junit.After +import org.junit.Before +import org.junit.Test +import kotlin.test.assertEquals + +@OptIn(ExperimentalCoroutinesApi::class) +class MainViewModelTest { + + private lateinit var useCase: GetQuoteUseCase + + private val viewModel: MainViewModel by lazy { + MainViewModel(useCase) + } + + @Before + fun setUp() { + useCase = mockk() + Dispatchers.setMain(Dispatchers.Unconfined) + } + + @After + fun tearDown() { + Dispatchers.resetMain() + } + + @Test + fun loadQuotesData_should_update_state_with_quotes_on_success() = runTest { + val quotes = listOf(Quote(id = "1", content = "Test Quote", author = "Author")) + val quoteResult = QuoteResult(results = quotes) + coEvery { useCase.dbQuotes() } returns flowOf(quotes) + coEvery { useCase.result() } returns ResponseResult.Success(quoteResult) + + viewModel.loadQuotesData() + advanceUntilIdle() + + assertEquals(quotes, viewModel.state.value.quotes) + assertEquals(false, viewModel.state.value.isLoading) + assertEquals("Test Quote", viewModel.state.value.quotes[0].content) + } + + @Test + fun loadQuotesData_should_emit_error_event_on_failure() = runTest{ + val exception = Exception("Test Exception") + coEvery { useCase.dbQuotes() } returns flowOf(emptyList()) + coEvery { useCase.result() } returns ResponseResult.Error(exception) + + viewModel.loadQuotesData() + advanceUntilIdle() + + assertEquals(emptyList(), viewModel.state.value.quotes) + assertEquals(false, viewModel.state.value.isLoading) + + } +} \ No newline at end of file diff --git a/Mani-Quotify/gradle/libs.versions.toml b/Mani-Quotify/gradle/libs.versions.toml index 29f312e..45c5653 100644 --- a/Mani-Quotify/gradle/libs.versions.toml +++ b/Mani-Quotify/gradle/libs.versions.toml @@ -16,6 +16,9 @@ retrofit = "2.11.0" jsonConverter = "1.6.0" ktxConvertor = "1.0.0" okhttp = "4.11.0" +kotlinTestJunit = "1.6.10" +mockk = "1.12.0" +kotlinxCoroutinesTest = "1.6.4" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -30,6 +33,9 @@ androidx-room-runtime = { group = "androidx.room", name = "room-runtime", versio androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "roomVersion" } androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "roomVersion" } # Kotlin serialization +kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlinTestJunit" } +kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutinesTest" } +mockk = { module = "io.mockk:mockk", version.ref = "mockk" } ktx-sxn-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "jsonConverter" } ktx-sxn-converter = { module = "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter", version.ref = "ktxConvertor" } okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } From 62d5604bdb0ea4b544b9fe9e47ed31915b5f7d98 Mon Sep 17 00:00:00 2001 From: itmani1990 Date: Sun, 13 Oct 2024 18:09:57 -0500 Subject: [PATCH 12/12] jacoco_setup and its coverage --- Mani-Quotify/app/build.gradle.kts | 65 +++++++++++++++++++ .../com/mani/quotify007/ExampleUnitTest.kt | 17 ----- .../navigation/viewmodel/MainViewModelTest.kt | 17 +++++ 3 files changed, 82 insertions(+), 17 deletions(-) delete mode 100644 Mani-Quotify/app/src/test/java/com/mani/quotify007/ExampleUnitTest.kt diff --git a/Mani-Quotify/app/build.gradle.kts b/Mani-Quotify/app/build.gradle.kts index 49688a5..55d001f 100644 --- a/Mani-Quotify/app/build.gradle.kts +++ b/Mani-Quotify/app/build.gradle.kts @@ -1,8 +1,11 @@ +import java.util.Locale + plugins { alias(libs.plugins.androidApplication) alias(libs.plugins.jetbrainsKotlinAndroid) alias(libs.plugins.ksp) alias(libs.plugins.kotlinSerialization) + jacoco } android { @@ -23,6 +26,10 @@ android { } buildTypes { + debug { + enableAndroidTestCoverage = true + enableUnitTestCoverage = true + } release { isMinifyEnabled = false proguardFiles( @@ -80,4 +87,62 @@ dependencies { testImplementation(libs.kotlinx.coroutines.test) testImplementation(libs.mockk) testImplementation(libs.kotlin.test.junit) +} + +val exclusions = listOf( + "**/R.class", + "**/R\$*.class", + "**/BuildConfig.*", + "**/Manifest*.*", + "**/*Test*.*", + "**/com/mani/quotify007/data/*", + "**/com/mani/quotify007/domain/*", + "**/com/mani/quotify007/ui/screens/*" +) + +tasks.withType(Test::class) { + configure { + isIncludeNoLocationClasses = true + excludes = listOf("jdk.internal.*", "sun/util/resources/cldr/provider/CLDRLocaleDataMetaInfo") + } +} + +android { + applicationVariants.all { + val variant = name.replaceFirstChar { + if (it.isLowerCase()) it.titlecase( + Locale.getDefault() + ) else it.toString() + } + + val unitTests = "test${variant}UnitTest" + val androidTests = "connected${variant}AndroidTest" + + tasks.register("Jacoco${variant}CodeCoverage") { + dependsOn(listOf(unitTests, androidTests)) + group = "Reporting" + description = "Execute ui and unit tests, generate and combine Jacoco coverage report" + reports { + xml.required.set(true) + html.required.set(true) + } + sourceDirectories.setFrom( + files( + "src/main/java", + "src/$variant/java" + ) + ) + classDirectories.setFrom(files( + fileTree(layout.buildDirectory.dir("intermediates/javac/")) { + exclude(exclusions) + }, + fileTree(layout.buildDirectory.dir("tmp/kotlin-classes/")) { + exclude(exclusions) + } + )) + executionData.setFrom(files( + fileTree(layout.buildDirectory) { include(listOf("**/*.exec", "**/*.ec")) } + )) + } + } } \ No newline at end of file diff --git a/Mani-Quotify/app/src/test/java/com/mani/quotify007/ExampleUnitTest.kt b/Mani-Quotify/app/src/test/java/com/mani/quotify007/ExampleUnitTest.kt deleted file mode 100644 index 90945eb..0000000 --- a/Mani-Quotify/app/src/test/java/com/mani/quotify007/ExampleUnitTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.mani.quotify007 - -import org.junit.Test - -import org.junit.Assert.* - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} \ No newline at end of file diff --git a/Mani-Quotify/app/src/test/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModelTest.kt b/Mani-Quotify/app/src/test/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModelTest.kt index 8e88652..4b83b75 100644 --- a/Mani-Quotify/app/src/test/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModelTest.kt +++ b/Mani-Quotify/app/src/test/java/com/mani/quotify007/ui/navigation/viewmodel/MainViewModelTest.kt @@ -3,12 +3,14 @@ package com.mani.quotify007.ui.navigation.viewmodel import com.mani.quotify007.domain.model.Quote import com.mani.quotify007.domain.model.QuoteResult import com.mani.quotify007.domain.usecase.GetQuoteUseCase +import com.mani.quotify007.ui.navigation.model.MainEvent import com.mani.quotify007.ui.navigation.model.ResponseResult import io.mockk.coEvery import io.mockk.mockk import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.runTest @@ -66,4 +68,19 @@ class MainViewModelTest { assertEquals(false, viewModel.state.value.isLoading) } + + @Test + fun `onEvent CopyText should emit UiActionEvent CopyText`() = runTest(UnconfinedTestDispatcher()) { + val quote = Quote(id = "1", content = "Test Quote", author = "Author") + + val quotes = listOf(Quote(id = "1", content = "Test Quote", author = "Author")) + val quoteResult = QuoteResult(results = quotes) + coEvery { useCase.dbQuotes() } returns flowOf(quotes) + coEvery { useCase.result() } returns ResponseResult.Success(quoteResult) + + viewModel.onEvent(MainEvent.CopyText(quote)) + viewModel.onEvent(MainEvent.ShareClick(quote)) + viewModel.onEvent(MainEvent.ShowToast("quote")) + } + } \ No newline at end of file