Ín my app's room database I have a table called movie_table. When the user clicks the item, it is saved to another table called favorite_table. I tried almost two days but I can't solve it. I am new to MVVM.
For better understanding please see the code:
FavoriteMovieDao.kt
#Dao
interface FavoriteMovieDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertFavMovie(favorite: Favorite)
#Query("SELECT * FROM favorite_table WHERE id LIKE :id")
suspend fun getFavoriteMovieById(id:Int): Favorite
#Query("SELECT * FROM favorite_table")
suspend fun getAllFavoriteMovie(): List<Favorite>
#Delete
suspend fun deleteFavorite(favorite: Favorite)
}
Repository.kt
class Repository(context: Context) {
private val favoriteMovieDao: FavoriteMovieDao = MovieDatabase.invoke(context).getFavoriteMovieDao()
suspend fun insertFavMovie(favorite: Favorite){
favoriteMovieDao.insertFavMovie(favorite)
}
MovieDetailsViewModel.kt
class MovieDetailsViewModel(private val repository: Repository):ViewModel() {
private val favMovieResponse:MutableLiveData<List<Favorite>> = MutableLiveData()
val actorResponse:MutableLiveData<List<Movie>> = MutableLiveData()
private val insertFavMovie:MutableLiveData<Favorite> = MutableLiveData()
fun actorDetail(){
viewModelScope.launch {
val actor = repository.getAllMovieDB()
actorResponse.value = actor
}
}
fun insertFavMovie(favorite: Favorite){
viewModelScope.launch {
//i can't insert this
val insertFav = repository.insertFavMovie(favorite)
insertFavMovie.value = insertFav
}
}
}
}
MovieDetailsActivity.kt
class MovieDetailsActivity : AppCompatActivity(){
private lateinit var actorViewModel: MovieDeatilsViewModel
private val actorAdapter by lazy { ActorAdapter() }
private var isFav: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_movie_details)
val repository = Repository(this)
val viewModelFactory = MovieDetailsViewModelFactory(repository)
actorViewModel = ViewModelProvider(this,viewModelFactory).get(MovieDeatilsViewModel::class.java)
setUpPostRecyclerView()
actorViewModel.actorDetail()
actorViewModel.actorResponse.observe(this, Observer {actorList ->
actorAdapter.setData(actorList)
})
initBundle()
favBtn.setOnClickListener {
if (isFav){
isFav = false
favBtn.supportImageTintList = ColorStateList.valueOf(Color.parseColor("#E4C1C1"))
Log.d("msg","Not Favorite!")
}else{
isFav = true
favBtn.supportImageTintList = ColorStateList.valueOf(Color.parseColor("#FFC107"))
Log.d("msg","Favorite!")
}
}
}
private fun initBundle() {
val bundle:Bundle? = intent.extras
movieTitle.text = bundle!!.getString("title")
directorbc.text = bundle.getString("director")
genre.text = bundle.getString("genre")
releaseYear.text = bundle.getInt("year").toString()
language.text = bundle.getString("language")
country.text = bundle.getString("country")
rating.text = bundle.getString("rating")
plotText.text = bundle.getString("plot")
//moviePosterD.setImageDrawable(bundle!!.getString("image"))
Glide.with(this).load(bundle.getString("image")).into(moviePosterD);
val player = SimpleExoPlayer.Builder(this).build()
player.preparePlayer(movieView)
player.setSource(applicationContext, "http://html5videoformatconverter.com/data/images/happyfit2.mp4")
}
private fun setUpPostRecyclerView(){
actorRecyclerview.adapter = actorAdapter
actorRecyclerview.layoutManager = LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false)
}
}
How can I insert it? Please help me. Thank you
In Movies table, to handle the favorite movies, you can make use of a variable in each movie item which you can for example set to true or false. This allows you to query using filters to show them on the screen. I don't think you need another table to store Favourites. I hope that helps! I can help you with the design if you share more details there.
#Entity
data class Movie( val title: String, val isFavourite: Boolean = false)
Update 1:
When user clicks on favButton, in MovieViewModel update the Movie entity like this:
fun setMovie(movie: Movie){
_movie.value = movie
}
fun updateMovie(movie: Movie) {
movie.isFavourite = !movie.isFavourite
viewModelScope.launch {
repository.updateMovie(movie)
setMovie(movie)
}
}
in MovieDao
#Update
suspend fun updateMovie(movie: Movie)
Update 2:
In MoviesDao
#Update
suspend fun updateMovie(movie: Movie)
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertAll(vararg movie: Movie)
#Query("select * from movies_table where isFavourite = 1")
fun getFavMovies(): LiveData<List<Movie>>
#Query("Select * from movies_table")
fun getAllMovies(): LiveData<List<Movie>>
In Repository
val favMovies: LiveData<List<Movie>> = moviesDao.getFavMovies()
Update 3:
favBtn.setOnClickListener {
_viewModel.updateMovie(movie)
}
_viewModel.movie.observe(viewLifecycleOwner, Observer {
//Update UI based on isFavourite value on Movie
})
Related
im really new on AAC and repository.
I have made an app with MVVM and repository.
Activity
class UserTestActivity : AppCompatActivity() {
private val userViewModel : UserViewModel by viewModel<UserViewModel>()
private lateinit var button : AppCompatButton
private var count : Int =0
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user_test
button = findViewById(R.id.testButton)
val userObserver = Observer<MutableList<UserModel>> { newUserList ->
Log.d("room-db-status", "size: "+newUserList.size)
}
userViewModel._user.observe(this, userObserver)
button.setOnClickListener(View.OnClickListener {
count++
Toast.makeText(this, "updated: "+count, Toast.LENGTH_SHORT).show()
userViewModel.insertUser(UserModel(
uid = count.toString(),
nickName = "Alexar",
gender ="female",
age = 22,
birth ="19990901",
mainResidence= "Seoul",
subResidence = "???",
tripWish = mutableListOf("!!!","!!?"),
tripStyle = mutableListOf("!!!","!!?"),
selfIntroduction = "hi -_-",
uriList = mutableListOf("!!!","!!?"),
geohash = "none",
latitude = 37.455,
longitude = 124.890,
mannerScore = 4.5,
premiumOrNot = false,
knock = 0
))
})
}
}
this is ViewModel
class UserViewModel (
private val userRepository : UserRepository): ViewModel() {
val _user : LiveData<MutableList<UserModel>> = liveData(Dispatchers.IO) {
val data = userRepository.getAllUser()
emit(data)
}
fun insertUser (userModel: UserModel) {
viewModelScope.launch(Dispatchers.IO) {
userRepository.insertUser(userModel)
}
}
}
Repositoty
interface UserRepository {
suspend fun getAllUser () : MutableList<UserModel>
suspend fun insertUser (userModel: UserModel)
}
RepositoryImpl
class UserRepositoryImpl (
private val localDataSource : UserLocalDataSource,
private val userMapper: UserMapper) :UserRepository{
override suspend fun getAllUser() : MutableList<UserModel> {
val userList : List<UserEntity> = localDataSource.getAllUser()
var temp = mutableListOf<UserModel>()
for (user in userList)
temp.add(userMapper.entityToModel(user))
return temp
}
override suspend fun insertUser(userModel: UserModel) {
return localDataSource.insertUser(userMapper.modelToEntity(userModel))
}
}
UserLocalDataSource
interface UserLocalDataSource {
suspend fun getAllUser () : MutableList<UserEntity>
suspend fun insertUser (userEntity: UserEntity)
}
UserLocalDataSourceImpl
class UserLocalDataSourceImpl(
private val appDatabase: AppDatabase) : UserLocalDataSource {
override suspend fun getAllUser() : MutableList<UserEntity> {
return appDatabase.userEntityDao().getAllUser()
}
override suspend fun insertUser(userEntity: UserEntity) {
appDatabase.userEntityDao().insertUser(userEntity)
}
}
UserEntityDao
interface UserEntityDAO {
#Query ("SELECT * FROM user " )
suspend fun getAllUser() : MutableList<UserEntity>
#Query ("SELECT * FROM user WHERE uid = (:uid) ")
suspend fun getUser(uid: String) :UserEntity
#Insert (onConflict = REPLACE)
suspend fun insertUser (user : UserEntity)
#Query("DELETE FROM user WHERE uid = (:uid)")
suspend fun delete(uid : String)
}
there are also Mapper and Koin injection.
when trying to insert user data to room, it was successful. but
after that, liveData Builder
val _user : LiveData<MutableList<UserModel>> = liveData(Dispatchers.IO) {
val data = userRepository.getAllUser()
emit(data)
}
not invoked...
Of course, that builder is invoked only once when app started haha
who knows why??
I do not know.
You shouldn't rely on the liveData builder to be invoked when data is inserted/updated in DB. Please refer to this doc on how to work with liveData builder.
To achieve the case when an observer of LiveData is invoked when data is inserted/updated in DB, methods must return LiveData object in UserEntityDao:
interface UserEntityDAO {
#Query ("SELECT * FROM user " )
suspend fun getAllUser() : LiveData<List<UserEntity>>
#Query ("SELECT * FROM user WHERE uid = (:uid) ")
suspend fun getUser(uid: String) : LiveData<UserEntity>
// ...
}
When I simply Insert my data for the first time, all is well and when I reload my tiles, they get saved in the database and the tiles reload normally - see Animation 1.
When I try to update my tiles, I have this weird behaviour, like a loop - see Animation 2.
I am wondering what could be causing this. Here is my code:
#Entity (tableName = "saved_values_table")
data class SavedValues (
#PrimaryKey()
var index: Int,
var visible: Boolean,
var enabled: Boolean,
var boardLetters: String,
var boardLettersValue: Int
)
Dao:
#Update
suspend fun updateValues(values: SavedValues)
Repo:
suspend fun updateValues(savedValues: SavedValues) {
savedValuesDao.updateValues(savedValues)
}
ViewModel:
fun saveValues(index: Int, boardLetters: Array<Letter>) {
val savedValues = readAllValues.value
if (savedValues != null) {
if (savedValues.isEmpty()) {
addValues(
SavedValues(
index,
visible = true,
enabled = true,
boardLetters = boardLetters[index].name,
boardLettersValue = boardLetters[index].value
)
)
}
else {
updateValues(
SavedValues(
index,
visible = true,
enabled = true,
boardLetters = boardLetters[index].name,
boardLettersValue = boardLetters[index].value
)
)
}
}
}
What am I doing wrong? Thanks for any help!
UPDATE
I am adding some more code here as the problem persists, contrary to what I said in my comment.
My whole Dao:
#Dao
interface SavedValuesDao {
#Insert
suspend fun addValues(values: SavedValues)
#Query("SELECT * FROM saved_values_table")
fun readAllValues(): LiveData<List<SavedValues>>
#Query("SELECT boardLetters FROM saved_values_table")
fun readBoardLetters(): LiveData<List<String>>
#Query("SELECT boardLettersValue FROM saved_values_table")
fun readBoardLettersValues (): LiveData<List<Int>>
#Update
suspend fun updateValues(values: SavedValues)
#Query("DELETE FROM saved_values_table")
suspend fun deleteAllValues()
}
My Database:
#Database(
entities = [Word::class, SavedValues::class, SavedWords::class],
version = 8,
exportSchema = false
)
abstract class WordDatabase : RoomDatabase() {
abstract fun wordDAO(): WordDao
abstract fun savedValuesDao(): SavedValuesDao
}
My Repository:
val readAllValues: LiveData<List<SavedValues>> = savedValuesDao.readAllValues()
val readBoardLetters: LiveData<List<String>> = savedValuesDao.readBoardLetters()
val readBoardLettersValues: LiveData<List<Int>> =
savedValuesDao.readBoardLettersValues()
suspend fun addValues(savedValues: SavedValues) {
savedValuesDao.addValues(savedValues)
}
suspend fun updateValues(savedValues: SavedValues) {
savedValuesDao.updateValues(savedValues)
}
suspend fun deleteAllValues() {
savedValuesDao.deleteAllValues()
}
Thanks for any help!
i want to use SearchView for search some element in room db and i have a problem with this because i cant use getFilter in RecyclerViewAdapter because i have ViewModel maybe whoes know how to combine all of this element in one project.
I search one way use Transormations.switchMap. But I couldn’t connect them.
ProductViewModel
class ProductViewModel(application: Application) : AndroidViewModel(application) {
private val repository: ProductRepository
val allProducts: LiveData<List<ProductEntity>>
private val searchStringLiveData = MutableLiveData<String>()
init {
val productDao = ProductsDB.getDatabase(application, viewModelScope).productDao()
repository = ProductRepository(productDao)
allProducts = repository.allProducts
searchStringLiveData.value = ""
}
fun insert(productEntity: ProductEntity) = viewModelScope.launch {
repository.insert(productEntity)
}
val products = Transformations.switchMap(searchStringLiveData) { string ->
repository.getAllListByName(string)
}
fun searchNameChanged(name: String) {
searchStringLiveData.value = name
}
}
ProductDao
interface ProductDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertProduct(productEntity: ProductEntity)
#Query("SELECT * from products")
fun getListAllProducts(): LiveData<List<ProductEntity>>
#Query("DELETE FROM products")
suspend fun deleteAll()
#Query("SELECT * FROM products where product_name_ent LIKE :name or LOWER(product_name_ent) like LOWER(:name)")
fun getListAllByName(name: String):LiveData<List<String>>
}
ProductDao
#Query("SELECT * FROM products where product_name_ent LIKE :name or LOWER(product_name_ent) like LOWER(:name)")
fun getListAllByName(name: String):LiveData<List<ProductEntity>>
This Method in your dao should return LiveData<List<ProductEntity>> and not LiveData<List<String>>, because this query selects everything (*) from the entity and not a specific column.
it is similar to (#Query("SELECT * from products") fun getListAllProducts():LiveData<List<ProductEntity>>)
ProductViewModel
class ProductViewModel(application: Application) : AndroidViewModel(application) {
private val repository: ProductRepository
init {
val productDao = ProductsDB.getDatabase(
application,
viewModelScope
).productDao()
repository = ProductRepository(productDao)
}
private val searchStringLiveData = MutableLiveData<String>("") //we can add initial value directly in the constructor
val allProducts: LiveData<List<ProductEntity>>=Transformations.switchMap(searchStringLiveData)
{
string->
if (TextUtils.isEmpty(string)) {
repository.allProducts()
} else {
repository.allProductsByName(string)
}
}
fun insert(productEntity: ProductEntity) = viewModelScope.launch {
repository.insert(productEntity)
}
fun searchNameChanged(name: String) {
searchStringLiveData.value = name
}
}
Repository
...with the other method that you have, add the following:
fun allProducts():LiveData<List<ProductEntity>>=productDao.getListAllProducts()
fun allProductsByNames(name:String):LiveData<List<ProductEntity>>=productDao.getListAllByName(name)
In Your Activity Or Fragment Where you have the recyclerview adapter
inside onCreate() (if it is an activity)
viewModel.allProducts.observe(this,Observer{products->
//populate your recyclerview here
})
or
onActivityCreated(if it is a fragment)
viewModel.allProducts.observe(viewLifecycleOwner,Observer{products->
//populate your recyclerview here
})
Now set a listener to the searchView, everytime the user submit the query , call viewModel.searchNameChanged(// pass the new value)
Hope this helps
Regards,
I am using this command to get data from a Room database:
select * from location_search_results where searchQuery = "wilmington"
Here is what the database looks like:
And here are the search results:
I have verified that the name of the table is correct and everything, and there's no spelling errors that I can find, so why would this query not return any of the three rows that it should match?
EDIT for code:
The source code is publicly available here, I am working in the mvvm branch, so if you pull it, make sure you're there. Below are the relevant classes:
LocationSearchResponse.kt:
#Entity(tableName = "location_search_results")
class LocationSearchResponse(
#ColumnInfo(name = "type")
val type: String,
#TypeConverters(DataConverter::class)
#SerializedName("query")
val searchQuery: List<String>,
#TypeConverters(DataConverter::class)
val features: List<Feature>,
#ColumnInfo(name = "attribution")
val attribution: String
) {
#PrimaryKey(autoGenerate = true)
var id: Int = 0
}
LocationSearchRepositoryImpl.kt:
class LocationSearchRepositoryImpl (
private val locationResponseDao: LocationResponseDao,
private val locationNetworkDataSource: LocationNetworkDataSource
): LocationSearchRepository {
init {
locationNetworkDataSource.downloadedLocationSearchResults.observeForever { locationResults ->
persistFetchedLocations(locationResults)
}
}
// update search data in db if necessary, then return the data that was searched for.
override suspend fun searchForLocation(query: String): LiveData<out LocationSearchResponse> {
return withContext(Dispatchers.IO) {
initSearch(query)
return#withContext locationResponseDao.searchForLocation(query)
}
}
// if a fetch is necessary (query has not already been searched), fetch search results
private suspend fun initSearch(query: String) {
if (isFetchLocationResultsNeeded(query))
fetchLocationResults(query)
}
private fun isFetchLocationResultsNeeded(query: String) : Boolean {
// get the cached results. If it's null, return true because it needs to be updated
val cachedResults = locationResponseDao.searchForLocationNonLive(query.toLowerCase())
if (cachedResults == null) return true
// if the results are empty, it needs to be fetched, else it doesn't
return cachedResults.features.isEmpty()
}
private suspend fun fetchLocationResults(query: String) {
locationNetworkDataSource.fetchLocationSearchResults("mapbox.places", query)
}
private fun persistFetchedLocations(fetchedLocationResults: LocationSearchResponse) {
GlobalScope.launch(Dispatchers.IO) {
locationResponseDao.upsert(fetchedLocationResults)
}
}
}
LocationResponseDao.kt:
#Dao
interface LocationResponseDao {
// update or insert existing entry if there is a conflict when adding to db
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun upsert(locationResults: LocationSearchResponse)
#Query("select * from location_search_results WHERE searchQuery = :query")
fun searchForLocation(query: String): LiveData<LocationSearchResponse>
#Query("select * from location_search_results WHERE searchQuery = :query")
fun searchForLocationNonLive(query: String): LocationSearchResponse?
#Query("delete from location_search_results")
fun nukeTable()
}
and ChooseCityFragment.kt:
class ChooseCityFragment : ScopedFragment(), KodeinAware {
override val kodein by closestKodein()
private val locationViewModelFactory: LocationResponseViewModelFactory by instance()
private val weatherResponseViewModelFactory: WeatherResponseViewModelFactory by instance()
private lateinit var locationViewModel: LocationResponseViewModel
private lateinit var weatherViewModel: WeatherResponseViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
setupViews()
// Inflate the layout for this fragment
return inflater.inflate(R.layout.choose_city_fragment, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
locationViewModel = ViewModelProviders.of(this, locationViewModelFactory)
.get(LocationResponseViewModel::class.java)
weatherViewModel = ViewModelProviders.of(this, weatherResponseViewModelFactory)
.get(WeatherResponseViewModel::class.java)
updateToolbar()
}
fun updateToolbar() {
(activity as? AppCompatActivity)?.supportActionBar?.title = "Choose Location"
(activity as? AppCompatActivity)?.supportActionBar?.subtitle = null
}
fun bindUI() = launch {
val locationResults = locationViewModel.locationResponse
val owner = viewLifecycleOwner
locationResults.observe(owner, Observer {
if (it == null) return#Observer
// TODO: set loading icon to GONE
initRecyclerView(it.features.toLocationSearchResultListItem())
})
}
fun setupViews() = launch {
search_button.setOnClickListener {
searchLocations()
search_results_rv.adapter?.notifyDataSetChanged()
}
}
// TODO: search text can not be more than 20 words or more than 256 characters. Need to account for this
fun searchLocations() = launch {
val searchText = search_box.text.toString()
if (searchText != "") {
locationViewModel.searchLocation(search_box.text.toString())
bindUI()
} else
Toast.makeText(context?.applicationContext, "Please enter a search term", Toast.LENGTH_SHORT).show()
}
private fun List<Feature>.toLocationSearchResultListItem() : List<LocationSearchResultListItem> {
return this.map {
LocationSearchResultListItem(it)
}
}
private fun initRecyclerView(items: List<LocationSearchResultListItem>) {
val groupAdapter = GroupAdapter<ViewHolder>().apply {
addAll(items)
}
groupAdapter.notifyDataSetChanged()
search_results_rv.apply {
layoutManager = LinearLayoutManager(this#ChooseCityFragment.context)
adapter = groupAdapter
}
groupAdapter.setOnItemClickListener { item, view ->
(item as? LocationSearchResultListItem)?.let {
refreshWeather(it.feature.coordinates[0], it.feature.coordinates[1])
}
}
}
private fun refreshWeather(latitude: Double, longitude: Double) = launch {
weatherViewModel.refreshWeatherWithCoordinates(latitude, longitude)
}
}
It turns out there was a space being added to the end of the searchQuery that I wasn't able to see. Once I figured out where my code was adding that space, I removed it and now everything looks good.
try something like this
it`s a dao interface example
use big letters in your query
#Query("SELECT * FROM person WHERE favoriteColor LIKE :color")
List<Person> getAllPeopleWithFavoriteColor(String color);
more info here
I'm building a debt application in Android using Dagger 2, Room and MVVM. My problem lies with the reactivity of my main view, where a list of debts are shown and you can tick them off.
When this activity is launched all the debts are loaded correctly, however when I insert a new debt the view is not updated accordingly.
The strange part is that when I tick one of them off the view refreshes as expected.
After much debugging I can infer that the problem has to do with the Lifecycle of the ViewModel, because the debts are created using a background job. If I hardcode in the constructor of the ViewModel a new insertion in the database the view updates as expected.
Here is my code for the Room Dao:
#Dao
interface DebtDao {
#Insert(onConflict = OnConflictStrategy.ABORT)
fun insert(debt: Debt)
#Query("SELECT * FROM debts WHERE id=:debtId")
fun findOne(debtId: Long): Debt
#Query("SELECT * FROM debts")
fun findAll(): LiveData<List<Debt>>
#Query("""
SELECT
debts.*,
p.name AS product_name,
d.name AS debtor_name,
c.name AS creditor_name
FROM debts
INNER JOIN products p ON debts.product_id = p.id
INNER JOIN debtors d ON debts.debtor_id = d.id
INNER JOIN creditors c ON debts.creditor_id = c.id
""")
fun findAllWithProductDebtorAndCreditor(): LiveData<List<DebtWithDebtorCreditorAndProduct>>
#Update
fun update(debt: Debt)
}
The activity:
class DebtsListActivity : AppCompatActivity() {
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val mainViewModel = ViewModelProviders.of(this, viewModelFactory).get(DebtsListViewModel::class.java)
val debtsListAdapter = DebtsListAdapter(ArrayList()) {
mainViewModel.payDebt(it.debt.id)
showSuccess("Debt updated with success!")
}
mainViewModel.formattedDebts.observe(this, Observer<List<DebtWithDebtorCreditorAndProduct>> {
if (it != null) {
// This is only invoked when launching initially the activity or then ticking on of the debts as paid not when inserting a new debt
debtsListAdapter.addItems(mainViewModel.getUnpaidDebts())
}
})
rvDebts.layoutManager = LinearLayoutManager(this)
rvDebts.adapter = debtsListAdapter
}
}
The view model:
class DebtsListViewModel #Inject constructor(var debtDao: DebtDao) : ViewModel() {
private var debts: LiveData<List<Debt>> = debtDao.findAll()
var formattedDebts: LiveData<List<DebtWithDebtorCreditorAndProduct>> = Transformations.switchMap(debts) {
debtDao.findAllWithProductDebtorAndCreditor()
}
fun payDebt(debtId: Long) {
val paidDebt = debtDao.findOne(debtId)
debtDao.update(paidDebt.copy(paid = true))
}
fun getUnpaidDebts(): List<DebtWithDebtorCreditorAndProduct> =
formattedDebts.value?.filter { !it.debt.paid }.orEmpty()
}
What I would like to do is to notify a formatted debt list containing all the information I want.
Edit:
This is the code for the background job:
class GenerateDebtJobService : JobService() {
#Inject
lateinit var debtDao: DebtDao
#Inject
lateinit var productDao: ProductDao
#Inject
lateinit var productsDebtorsDao: ProductDebtorDao
#Inject
lateinit var productCreditorsDao: ProductCreditorDao
override fun onStartJob(params: JobParameters): Boolean {
DaggerGraphBuilder.build(applicationContext as FineApplication).inject(this)
val productId = params.extras.getLong("id")
val product = productDao.findOne(productId)
val productCreditor = productCreditorsDao.findOneByProduct(productId)
val debtors = productsDebtorsDao.findAllByProduct(productId)
// When the bill day is reached for a given product the debtors list associated with that product is looped through and a new debt is created
debtors.forEach {
debtDao.insert(Debt(productId = it.productProductId, debtorId = it.productDebtorId, quantity = product.recurringCost, date = DateTime().toString(), creditorId = productCreditor.productCreditorId))
}
return false
}
override fun onStopJob(params: JobParameters): Boolean {
Log.e("GenerateDebtJob", "job finished")
return false
}
companion object {
fun build(application: Application, productId: Long, periodicity: Days, startDay: Days) {
val serviceComponent = ComponentName(application, GenerateDebtJobService::class.java)
val bundle = PersistableBundle()
bundle.putLong("id", productId)
val builder = JobInfo.Builder(productId.toInt(), serviceComponent)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
// .setPeriodic(TimeUnit.DAYS.toMillis(periodicity.days.toLong()))
.setOverrideDeadline(1_000)
.setExtras(bundle)
(application.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler).schedule(builder.build())
}
}
}