diff --git a/app/src/main/java/com/ti/mobpo/AppViewModelProvider.kt b/app/src/main/java/com/ti/mobpo/AppViewModelProvider.kt index 84a07fc..81f6270 100644 --- a/app/src/main/java/com/ti/mobpo/AppViewModelProvider.kt +++ b/app/src/main/java/com/ti/mobpo/AppViewModelProvider.kt @@ -1,11 +1,11 @@ package com.ti.mobpo import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.createSavedStateHandle import androidx.lifecycle.viewmodel.CreationExtras import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory -import com.ti.mobpo.ui.pokesearch.PokeSearchViewModel +import com.ti.mobpo.ui.viewmodels.FavouritesViewModel +import com.ti.mobpo.ui.viewmodels.PokeSearchViewModel object AppViewModelProvider { val Factory = viewModelFactory { @@ -13,9 +13,17 @@ object AppViewModelProvider { PokeSearchViewModel( pokesearchApplication().appContainer.favouritesRepository ) + + } + + initializer { + FavouritesViewModel( + pokesearchApplication().appContainer.favouritesRepository, pokesearchApplication().featureManager + ) } } } fun CreationExtras.pokesearchApplication(): PokeSearch = - (this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as PokeSearch) \ No newline at end of file + (this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as PokeSearch) + diff --git a/app/src/main/java/com/ti/mobpo/PokeSearch.kt b/app/src/main/java/com/ti/mobpo/PokeSearch.kt index 3d45059..953cb2a 100644 --- a/app/src/main/java/com/ti/mobpo/PokeSearch.kt +++ b/app/src/main/java/com/ti/mobpo/PokeSearch.kt @@ -3,17 +3,26 @@ package com.ti.mobpo import android.app.Application import com.ti.mobpo.data.AppContainer import com.ti.mobpo.data.AppDataContainer +import com.ti.mobpo.ui.util.FeatureManager class PokeSearch : Application() { lateinit var appContainer: AppContainer private set + lateinit var featureManager: FeatureManager + private set + override fun onCreate() { super.onCreate() appContainer = createAppContainer() + featureManager = createFeatrureManager() } private fun createAppContainer(): AppContainer { return AppDataContainer(this) } + + private fun createFeatrureManager(): FeatureManager { + return FeatureManager(this) + } } diff --git a/app/src/main/java/com/ti/mobpo/ui/Navigation.kt b/app/src/main/java/com/ti/mobpo/ui/Navigation.kt index e09a3c7..3fb9343 100644 --- a/app/src/main/java/com/ti/mobpo/ui/Navigation.kt +++ b/app/src/main/java/com/ti/mobpo/ui/Navigation.kt @@ -20,18 +20,16 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview -import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController -import com.ti.mobpo.ui.screens.Favourites +import com.ti.mobpo.ui.screens.FavoritesScreen import com.ti.mobpo.ui.screens.PokeSearchScreen @Composable @@ -108,7 +106,7 @@ fun Navigation() { } composable( route = Screen.Favourites.route) { - Favourites() + FavoritesScreen() } } } diff --git a/app/src/main/java/com/ti/mobpo/ui/pokesearch/PokeSearchUiState.kt b/app/src/main/java/com/ti/mobpo/ui/pokesearch/PokeSearchUiState.kt deleted file mode 100644 index 5012bf5..0000000 --- a/app/src/main/java/com/ti/mobpo/ui/pokesearch/PokeSearchUiState.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.ti.mobpo.ui.pokesearch - -data class PokeSearchUiState(val searchQuery: String = "") diff --git a/app/src/main/java/com/ti/mobpo/ui/screens/Favourites.kt b/app/src/main/java/com/ti/mobpo/ui/screens/Favourites.kt index dae1083..fe9d851 100644 --- a/app/src/main/java/com/ti/mobpo/ui/screens/Favourites.kt +++ b/app/src/main/java/com/ti/mobpo/ui/screens/Favourites.kt @@ -18,6 +18,8 @@ import com.ti.mobpo.ui.viewmodels.FavouritesViewModel import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.grid.items +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Modifier @Composable @@ -27,8 +29,11 @@ fun FavoritesScreen(viewModel: FavouritesViewModel = viewModel(factory = AppView @Composable fun Favorites(favoritesViewModel: FavouritesViewModel) { + LaunchedEffect(Unit) { + favoritesViewModel.loadFavourites() + } + val favorites by favoritesViewModel.pokemonDetails.collectAsState() - favoritesViewModel.loadFavourites() Column( verticalArrangement = Arrangement.Top, diff --git a/app/src/main/java/com/ti/mobpo/ui/screens/PokeSearch.kt b/app/src/main/java/com/ti/mobpo/ui/screens/PokeSearch.kt index a2aa2a7..cc238cb 100644 --- a/app/src/main/java/com/ti/mobpo/ui/screens/PokeSearch.kt +++ b/app/src/main/java/com/ti/mobpo/ui/screens/PokeSearch.kt @@ -1,6 +1,6 @@ package com.ti.mobpo.ui.screens -import com.ti.mobpo.ui.pokesearch.PokeSearchViewModel +import com.ti.mobpo.ui.viewmodels.PokeSearchViewModel import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -26,9 +26,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale @@ -94,7 +91,6 @@ fun PokeSearch(pokeSearchViewModel: PokeSearchViewModel) { @Composable fun PokemonCard(pokemon: PokemonDetails, toggleFavorite: (Int) -> Unit) { - var isFavourite by remember { mutableStateOf(false) } Card( shape = MaterialTheme.shapes.medium, elevation = CardDefaults.cardElevation(defaultElevation = 8.dp), diff --git a/app/src/main/java/com/ti/mobpo/ui/util/FeatureManager.kt b/app/src/main/java/com/ti/mobpo/ui/util/FeatureManager.kt new file mode 100644 index 0000000..df3bf9e --- /dev/null +++ b/app/src/main/java/com/ti/mobpo/ui/util/FeatureManager.kt @@ -0,0 +1,18 @@ +package com.ti.mobpo.ui.util + +import android.content.Context + +class FeatureManager(private val context: Context) { + private val PREFS_NAME = "FeaturePrefs" + private val KEY_PAID_FEATURE_ENABLED = "paid_feature_enabled" + + fun hasAccessToPaidFeature(): Boolean { + val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) + return prefs.getBoolean(KEY_PAID_FEATURE_ENABLED, false) + } + + fun setPaidFeatureEnabled(enabled: Boolean) { + val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) + prefs.edit().putBoolean(KEY_PAID_FEATURE_ENABLED, enabled).apply() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ti/mobpo/ui/viewmodels/FavouritesViewModel.kt b/app/src/main/java/com/ti/mobpo/ui/viewmodels/FavouritesViewModel.kt new file mode 100644 index 0000000..b829db6 --- /dev/null +++ b/app/src/main/java/com/ti/mobpo/ui/viewmodels/FavouritesViewModel.kt @@ -0,0 +1,76 @@ +package com.ti.mobpo.ui.viewmodels + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.ti.mobpo.data.Favourite +import com.ti.mobpo.data.FavouritesRepository +import com.ti.mobpo.model.PokemonDetails +import com.ti.mobpo.network.PokeApi +import com.ti.mobpo.ui.util.FeatureManager +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch +import java.io.IOException + +class FavouritesViewModel(private val favouritesRepository: FavouritesRepository, featureManager: FeatureManager) : ViewModel() { + private val service = PokeApi.retrofitService; + + private val _pokemonDetails = MutableStateFlow?>(null) + val pokemonDetails: StateFlow?> = _pokemonDetails.asStateFlow() + + fun loadFavourites() { + // Ugly workaround to make sure all the favourites are loaded before displaying them + viewModelScope.launch { + try { + val favouritesList: StateFlow = + favouritesRepository.getAllItems().map { FavouriteUiState(it) } + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000L), + initialValue = FavouriteUiState() + ) + favouritesList.collect { state -> + val detailsList = mutableListOf() + for (favourite in state.favourites) { + val details = service.getPokemonDetails(favourite.id) + detailsList.add(details.copy(isFavorite = true)) + } + _pokemonDetails.value = detailsList + } + } catch (e: IOException) { + /* Handle error */ + } + } + } + + + + + fun toggleFavorite(pokemonId: Int) { + _pokemonDetails.value = _pokemonDetails.value?.map { pokemon -> + if (pokemon.id == pokemonId) { + pokemon.copy(isFavorite = !pokemon.isFavorite) + } else { + pokemon + } + } + viewModelScope.launch { + if (_pokemonDetails.value != null) { + val pokemon = _pokemonDetails.value!!.find { it.id == pokemonId } + pokemon?.let { + if (it.isFavorite) { + favouritesRepository.insertItem(Favourite(it.id, it.name)) + } else { + favouritesRepository.deleteItem(Favourite(it.id, it.name)) + } + } + } + } + } +} + +data class FavouriteUiState(val favourites: List = listOf()) diff --git a/app/src/main/java/com/ti/mobpo/ui/pokesearch/PokeSearchViewModel.kt b/app/src/main/java/com/ti/mobpo/ui/viewmodels/PokeSearchViewModel.kt similarity index 98% rename from app/src/main/java/com/ti/mobpo/ui/pokesearch/PokeSearchViewModel.kt rename to app/src/main/java/com/ti/mobpo/ui/viewmodels/PokeSearchViewModel.kt index 5bdc5a6..cd1888a 100644 --- a/app/src/main/java/com/ti/mobpo/ui/pokesearch/PokeSearchViewModel.kt +++ b/app/src/main/java/com/ti/mobpo/ui/viewmodels/PokeSearchViewModel.kt @@ -1,4 +1,4 @@ -package com.ti.mobpo.ui.pokesearch +package com.ti.mobpo.ui.viewmodels import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope