Skip to content

Commit

Permalink
add coroutine flow and refactor code πŸ‘€
Browse files Browse the repository at this point in the history
  • Loading branch information
tomorisakura committed Dec 29, 2020
1 parent 75b01e1 commit 447d399
Show file tree
Hide file tree
Showing 23 changed files with 312 additions and 96 deletions.
1 change: 1 addition & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ __Masak Apa__
for this app i try to consume a [Masak Apa](https://github.com/tomorisakura/unofficial-masakapahariini-api)
this app build with MVVM pattern and Jetpack Component πŸš€

__status__ : onprogress πŸ§™β€β™‚οΈ

### Screen
---

Expand Down Expand Up @@ -44,4 +42,8 @@ __status__ : onprogress πŸ§™β€β™‚οΈ

- [x] Jetpack Navigation

- [x] Room
- [x] Room

- [x] Coroutine Flow

- [ ] ViewBinding
16 changes: 8 additions & 8 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ android {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

dataBinding {
enabled = true
buildFeatures {
viewBinding = true
}

buildTypes {
Expand All @@ -49,8 +49,8 @@ dependencies {
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.2'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
Expand All @@ -60,7 +60,7 @@ dependencies {
//gson and coroutines
implementation 'com.google.code.gson:gson:2.8.6'
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.20.0")
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1"

//fragment ktx and safe args
implementation "androidx.fragment:fragment-ktx:1.2.5"
Expand All @@ -76,17 +76,17 @@ dependencies {
//hilt and jetpack view model
implementation "com.google.dagger:hilt-android:2.28-alpha"
kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"
implementation 'androidx.fragment:fragment-ktx:1.3.0-beta01'
implementation 'androidx.fragment:fragment-ktx:1.3.0-rc01'
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02"
kapt "androidx.hilt:hilt-compiler:1.0.0-alpha02"

//swipe refresh
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'com.github.florent37:shapeofview:1.4.7'

implementation 'androidx.constraintlayout:constraintlayout:2.1.0-alpha1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.0-alpha2'

def room_version = "2.2.5"
def room_version = "2.2.6"

implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
Expand Down
9 changes: 8 additions & 1 deletion app/src/main/java/com/grevi/masakapa/db/RecipesDAO.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.grevi.masakapa.db

import androidx.room.*
import com.grevi.masakapa.db.entity.Category
import com.grevi.masakapa.db.entity.Recipes

@Dao
Expand All @@ -13,8 +14,14 @@ interface RecipesDAO {
suspend fun isExistsRecipes(key : String) : Boolean

@Query("SELECT * FROM recipes")
suspend fun getAllMarkRecipes() : List<Recipes>
suspend fun getAllMarkRecipes() : MutableList<Recipes>

@Delete
suspend fun deleteRecipes(recipes: Recipes)

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertCategory(vararg category: Category)

@Query("SELECT * FROM category")
suspend fun getAllCategory() : MutableList<Category>
}
10 changes: 7 additions & 3 deletions app/src/main/java/com/grevi/masakapa/db/RecipesDataSource.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package com.grevi.masakapa.db

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.grevi.masakapa.db.entity.Category
import com.grevi.masakapa.db.entity.Recipes
import kotlinx.coroutines.flow.Flow

interface RecipesDataSource {
suspend fun insertRecipes(recipes: Recipes)
suspend fun isExistRecipes(key : String) : Boolean
suspend fun getMarkRecipes() : List<Recipes>
suspend fun getMarkRecipes() : MutableList<Recipes>
suspend fun deleteRecipes(recipes: Recipes)
suspend fun getFlowRecipes() : Flow<List<Recipes>>
suspend fun insertCategory(category: Category)
suspend fun getAllCategory() : MutableList<Category>
suspend fun getFlowCategory() : Flow<MutableList<Category>>
}
25 changes: 22 additions & 3 deletions app/src/main/java/com/grevi/masakapa/db/RecipesDataSourceImpl.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,32 @@
package com.grevi.masakapa.db

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.grevi.masakapa.db.entity.Category
import com.grevi.masakapa.db.entity.Recipes
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import javax.inject.Inject

class RecipesDataSourceImpl @Inject constructor(private val recipesDAO: RecipesDAO) : RecipesDataSource {
override suspend fun insertRecipes(recipes: Recipes) = recipesDAO.insertRecipes(recipes)
override suspend fun isExistRecipes(key: String) : Boolean = recipesDAO.isExistsRecipes(key)
override suspend fun getMarkRecipes() : List<Recipes> = recipesDAO.getAllMarkRecipes()
override suspend fun getMarkRecipes() : MutableList<Recipes> = recipesDAO.getAllMarkRecipes()
override suspend fun deleteRecipes(recipes: Recipes) = recipesDAO.deleteRecipes(recipes)
override suspend fun getFlowRecipes(): Flow<List<Recipes>> {
return flow {
emit(recipesDAO.getAllMarkRecipes())
}
}

override suspend fun insertCategory(category: Category) = recipesDAO.insertCategory(category)

override suspend fun getAllCategory(): MutableList<Category> {
return recipesDAO.getAllCategory()
}

override suspend fun getFlowCategory(): Flow<MutableList<Category>> {
return flow {
emit(recipesDAO.getAllCategory())
}
}

}
3 changes: 2 additions & 1 deletion app/src/main/java/com/grevi/masakapa/db/RecipesDatabase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ package com.grevi.masakapa.db

import androidx.room.Database
import androidx.room.RoomDatabase
import com.grevi.masakapa.db.entity.Category
import com.grevi.masakapa.db.entity.Recipes

@Database(entities = [Recipes::class], version = 1, exportSchema = false)
@Database(entities = [Recipes::class, Category::class], version = 2, exportSchema = true)
abstract class RecipesDatabase : RoomDatabase() {
abstract fun recipesDAO() : RecipesDAO
}
11 changes: 11 additions & 0 deletions app/src/main/java/com/grevi/masakapa/db/entity/Category.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.grevi.masakapa.db.entity

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "category")
data class Category(
@PrimaryKey @ColumnInfo(name = "key") var key : String,
@ColumnInfo(name = "category") var category : String
)
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ open class SafeApiResponse {
is ConnectException -> Resource.error(null, "connection failure")
is IOException -> Resource.error(null,"no inet")
is OkHttpClient -> Resource.error(null, "unable host")
else -> error(e.message ?: e.toString())
else -> Resource.error(null, e.message)
}
}
}
Expand Down
13 changes: 10 additions & 3 deletions app/src/main/java/com/grevi/masakapa/repos/Remote.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package com.grevi.masakapa.repos

import androidx.lifecycle.LiveData
import com.grevi.masakapa.db.RecipesDataSource
import com.grevi.masakapa.db.entity.Category
import com.grevi.masakapa.db.entity.Recipes
import com.grevi.masakapa.network.data.ApiHelper
import com.grevi.masakapa.network.SafeApiResponse
import com.grevi.masakapa.network.data.ApiHelper
import com.grevi.masakapa.network.response.CategorysResponse
import com.grevi.masakapa.network.response.DetailResponse
import com.grevi.masakapa.network.response.RecipesResponse
import com.grevi.masakapa.network.response.SearchResponse
import com.grevi.masakapa.util.Resource
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject

class Remote @Inject constructor(private val apiHelper: ApiHelper, private val recipesDataSource: RecipesDataSource) : SafeApiResponse() {
Expand Down Expand Up @@ -42,9 +43,15 @@ class Remote @Inject constructor(private val apiHelper: ApiHelper, private val r
return recipesDataSource.isExistRecipes(key)
}

suspend fun getMarkedRecipes() : List<Recipes> {
suspend fun getMarkedRecipes() : MutableList<Recipes> {
return recipesDataSource.getMarkRecipes()
}

suspend fun deleteRecipes(recipes: Recipes) = recipesDataSource.deleteRecipes(recipes)

suspend fun getFlowRecipes() : Flow<List<Recipes>> = recipesDataSource.getFlowRecipes()

suspend fun getFlowCategory() : Flow<MutableList<Category>> = recipesDataSource.getFlowCategory()

suspend fun insertCategory(category: Category) = recipesDataSource.insertCategory(category)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.grevi.masakapa.R
import com.grevi.masakapa.db.entity.Category
import com.grevi.masakapa.model.Categorys
import com.grevi.masakapa.util.CategoryListenear
import kotlinx.android.synthetic.main.lists_categorys.view.*

class CategorysAdapter : RecyclerView.Adapter<CategorysAdapter.CategoryVH>() {

private val categorys : MutableList<Categorys> = mutableListOf()
private val categorys : MutableList<Category> = mutableListOf()
private var listenear : CategoryListenear? = null

inner class CategoryVH(view : View) : RecyclerView.ViewHolder(view) {
fun bind(categorys: Categorys) {
fun bind(categorys: Category) {
itemView.categorysText.text = categorys.category
itemView.setOnClickListener { listenear?.onItemSelected(categorys) }
}
Expand All @@ -25,7 +26,7 @@ class CategorysAdapter : RecyclerView.Adapter<CategorysAdapter.CategoryVH>() {
this.listenear = listenear
}

fun addItem(item : List<Categorys>) {
fun addItem(item : List<Category>) {
categorys.clear()
categorys.addAll(item)
notifyDataSetChanged()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class MarkAdapter : RecyclerView.Adapter<MarkAdapter.MarkVH>() {
fun removeItem(item: Recipes, position: Int) {
recipes.remove(item)
notifyItemRemoved(position)
notifyItemChanged(position)
notifyDataSetChanged()
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MarkVH {
Expand Down
66 changes: 32 additions & 34 deletions app/src/main/java/com/grevi/masakapa/ui/marked/MarkFragment.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
package com.grevi.masakapa.ui.marked

import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
Expand All @@ -17,20 +13,18 @@ import androidx.navigation.Navigation
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import com.grevi.masakapa.R
import com.grevi.masakapa.db.entity.Recipes
import com.grevi.masakapa.ui.adapter.MarkAdapter
import com.grevi.masakapa.ui.search.SearchActivity
import com.grevi.masakapa.ui.viewmodel.DatabaseViewModel
import com.grevi.masakapa.util.MarkListener
import com.grevi.masakapa.util.Resource
import com.grevi.masakapa.util.toast
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.android.synthetic.main.fragment_mark.*
import kotlinx.android.synthetic.main.fragment_recipes.*
import kotlinx.android.synthetic.main.snap_mark_layout.*
import java.text.FieldPosition

@AndroidEntryPoint
class MarkFragment : Fragment() {
Expand All @@ -57,13 +51,24 @@ class MarkFragment : Fragment() {
markAdapter = MarkAdapter()
rv_recipes_mark.layoutManager = LinearLayoutManager(this.context, LinearLayoutManager.VERTICAL, false)
rv_recipes_mark.adapter = markAdapter
databaseViewModel.lisMark.observe(viewLifecycleOwner, Observer {
if (!it.isNullOrEmpty()) {
markAdapter.addItem(it)
prepareSwipe(it)
showSnap(false)
} else {
showSnap(true)
databaseViewModel.listMark.observe(viewLifecycleOwner, Observer { response ->
when(response.status) {
Resource.Status.SUCCESS -> {
response.data?.let {
markAdapter.addItem(it)
prepareSwipe(it)
}
showSnap(false)
}

Resource.Status.LOADING -> {
toast(requireContext(), response.msg.toString())
}

Resource.Status.ERROR -> {
showSnap(true)
toast(requireContext(), response.msg.toString())
}
}
})

Expand All @@ -73,6 +78,14 @@ class MarkFragment : Fragment() {
}

})

databaseViewModel.state.observe(viewLifecycleOwner, Observer {
if (it) {
showSnap(it)
} else {
showSnap(it)
}
})
}

private fun prepareSwipe(recipes : List<Recipes>) {
Expand All @@ -87,9 +100,10 @@ class MarkFragment : Fragment() {

override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.adapterPosition
val msg = "Hapus ${recipes[position].name} ?"
val msg = "Resep ${recipes[position].name} dihapus"
markAdapter.removeItem(recipes[position], position)
materialDialog(requireContext(), msg, recipes[position], position).show()
databaseViewModel.deleteRecipes(recipes[position], position)
materialDialog(mainMark, msg).show()
}

}
Expand Down Expand Up @@ -122,23 +136,7 @@ class MarkFragment : Fragment() {
}


private fun materialDialog(context: Context, msg: String, recipes: Recipes, position : Int) : MaterialAlertDialogBuilder {
return MaterialAlertDialogBuilder(context)
.setTitle("Hapus Resep Dari List Favorite")
.setMessage(msg)
.setPositiveButton("Hapus") { dialog, which ->
Log.v("POSITION_RECIPES", position.toString())
databaseViewModel.deleteRecipes(recipes)
markAdapter.notifyItemRemoved(position)
markAdapter.notifyDataSetChanged()
dialog.dismiss()
}
.setNegativeButton("Batal") { dialog, which ->
Log.v("POSITION_UNDO", recipes.name)
markAdapter.restoreItem(recipes, position)
markAdapter.notifyItemChanged(position)
markAdapter.notifyDataSetChanged()
dialog.cancel()
}
private fun materialDialog(view: View, msg: String) : Snackbar {
return Snackbar.make(view, msg, Snackbar.LENGTH_SHORT)
}
}
Loading

0 comments on commit 447d399

Please sign in to comment.