Implement view model provider, local database
This commit is contained in:
parent
ec25b03e98
commit
77d7056962
@ -4,6 +4,7 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
|
android:name=".PokeSearch"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:enableOnBackInvokedCallback="true"
|
android:enableOnBackInvokedCallback="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
21
app/src/main/java/com/ti/mobpo/AppViewModelProvider.kt
Normal file
21
app/src/main/java/com/ti/mobpo/AppViewModelProvider.kt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
object AppViewModelProvider {
|
||||||
|
val Factory = viewModelFactory {
|
||||||
|
initializer {
|
||||||
|
PokeSearchViewModel(
|
||||||
|
pokesearchApplication().appContainer.favouritesRepository
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun CreationExtras.pokesearchApplication(): PokeSearch =
|
||||||
|
(this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as PokeSearch)
|
19
app/src/main/java/com/ti/mobpo/PokeSearch.kt
Normal file
19
app/src/main/java/com/ti/mobpo/PokeSearch.kt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package com.ti.mobpo
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import com.ti.mobpo.data.AppContainer
|
||||||
|
import com.ti.mobpo.data.AppDataContainer
|
||||||
|
|
||||||
|
class PokeSearch : Application() {
|
||||||
|
lateinit var appContainer: AppContainer
|
||||||
|
private set
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
appContainer = createAppContainer()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createAppContainer(): AppContainer {
|
||||||
|
return AppDataContainer(this)
|
||||||
|
}
|
||||||
|
}
|
@ -17,4 +17,7 @@ interface FavouriteDao {
|
|||||||
|
|
||||||
@Query("SELECT id FROM favourites")
|
@Query("SELECT id FROM favourites")
|
||||||
fun getAllFavoriteIds(): Flow<List<Int>>
|
fun getAllFavoriteIds(): Flow<List<Int>>
|
||||||
|
|
||||||
|
@Query("SELECT EXISTS(SELECT 1 FROM favourites WHERE id = :id LIMIT 1)")
|
||||||
|
suspend fun isFavourite(id: Int): Boolean
|
||||||
}
|
}
|
||||||
|
@ -20,4 +20,10 @@ interface FavouritesRepository {
|
|||||||
* Delete item from the data source
|
* Delete item from the data source
|
||||||
*/
|
*/
|
||||||
suspend fun deleteItem(item: Favourite)
|
suspend fun deleteItem(item: Favourite)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the item is a favourite
|
||||||
|
*/
|
||||||
|
|
||||||
|
suspend fun isFavourite(id: Int): Boolean
|
||||||
}
|
}
|
||||||
|
@ -13,4 +13,8 @@ class OfflineFavouritesRepository(private val favouriteDao: FavouriteDao) : Favo
|
|||||||
override suspend fun deleteItem(item: Favourite) {
|
override suspend fun deleteItem(item: Favourite) {
|
||||||
favouriteDao.delete(item)
|
favouriteDao.delete(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun isFavourite(id: Int): Boolean {
|
||||||
|
return favouriteDao.isFavourite(id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberUpdatedState
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
@ -30,7 +31,6 @@ import androidx.navigation.compose.NavHost
|
|||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import com.ti.mobpo.ui.pokesearch.PokeSearchViewModel
|
|
||||||
import com.ti.mobpo.ui.screens.Favourites
|
import com.ti.mobpo.ui.screens.Favourites
|
||||||
import com.ti.mobpo.ui.screens.PokeSearchScreen
|
import com.ti.mobpo.ui.screens.PokeSearchScreen
|
||||||
|
|
||||||
@ -61,8 +61,6 @@ fun Navigation() {
|
|||||||
val updatedSelectedIndex = items.indexOfFirst { it.route == currentRoute }
|
val updatedSelectedIndex = items.indexOfFirst { it.route == currentRoute }
|
||||||
selectedItemIndex = rememberUpdatedState(updatedSelectedIndex).value
|
selectedItemIndex = rememberUpdatedState(updatedSelectedIndex).value
|
||||||
|
|
||||||
val pokeSearchViewModel: PokeSearchViewModel = viewModel()
|
|
||||||
|
|
||||||
Scaffold (
|
Scaffold (
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
NavigationBar {
|
NavigationBar {
|
||||||
@ -106,7 +104,7 @@ fun Navigation() {
|
|||||||
ExitTransition.None
|
ExitTransition.None
|
||||||
}) {
|
}) {
|
||||||
composable(route = Screen.PokeSearch.route) {
|
composable(route = Screen.PokeSearch.route) {
|
||||||
PokeSearchScreen(pokeSearchViewModel)
|
PokeSearchScreen()
|
||||||
}
|
}
|
||||||
composable(
|
composable(
|
||||||
route = Screen.Favourites.route) {
|
route = Screen.Favourites.route) {
|
||||||
@ -124,7 +122,6 @@ fun Navigation() {
|
|||||||
@Composable
|
@Composable
|
||||||
fun NavigationPreview() {
|
fun NavigationPreview() {
|
||||||
Surface {
|
Surface {
|
||||||
val pokeSearchViewModel: PokeSearchViewModel = viewModel()
|
PokeSearchScreen()
|
||||||
PokeSearchScreen(pokeSearchViewModel)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,6 +2,8 @@ package com.ti.mobpo.ui.pokesearch
|
|||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
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.model.PokemonDetails
|
||||||
import com.ti.mobpo.network.PokeApi
|
import com.ti.mobpo.network.PokeApi
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
@ -12,7 +14,7 @@ import java.io.IOException
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PokeSearchViewModel : ViewModel() {
|
class PokeSearchViewModel(private val favouritesRepository: FavouritesRepository) : ViewModel() {
|
||||||
private val service = PokeApi.retrofitService;
|
private val service = PokeApi.retrofitService;
|
||||||
|
|
||||||
private val _pokemonDetails = MutableStateFlow<List<PokemonDetails>?>(null)
|
private val _pokemonDetails = MutableStateFlow<List<PokemonDetails>?>(null)
|
||||||
@ -26,7 +28,8 @@ class PokeSearchViewModel : ViewModel() {
|
|||||||
val detailsList = mutableListOf<PokemonDetails>()
|
val detailsList = mutableListOf<PokemonDetails>()
|
||||||
for (pokemonSpecies in filteredList) {
|
for (pokemonSpecies in filteredList) {
|
||||||
val details = service.getPokemonDetails(extractPokemonId(pokemonSpecies.url))
|
val details = service.getPokemonDetails(extractPokemonId(pokemonSpecies.url))
|
||||||
detailsList.add(details)
|
val isFavorite = favouritesRepository.isFavourite(details.id)
|
||||||
|
detailsList.add(details.copy(isFavorite = isFavorite))
|
||||||
}
|
}
|
||||||
_pokemonDetails.value = detailsList
|
_pokemonDetails.value = detailsList
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
@ -48,5 +51,17 @@ class PokeSearchViewModel : ViewModel() {
|
|||||||
pokemon
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,12 +45,13 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
|||||||
import com.ti.mobpo.model.PokemonDetails
|
import com.ti.mobpo.model.PokemonDetails
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
|
import com.ti.mobpo.AppViewModelProvider
|
||||||
import com.ti.mobpo.capitalizeFirstLetterAfterHyphens
|
import com.ti.mobpo.capitalizeFirstLetterAfterHyphens
|
||||||
import com.ti.mobpo.ui.theme.MobileSecurityTheme
|
import com.ti.mobpo.ui.theme.MobileSecurityTheme
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PokeSearchScreen(pokeSearchViewModel: PokeSearchViewModel) {
|
fun PokeSearchScreen(viewModel: PokeSearchViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
|
||||||
PokeSearch(pokeSearchViewModel)
|
PokeSearch(viewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
Loading…
x
Reference in New Issue
Block a user