Skip to content

Commit 3bb1a39

Browse files
Merge branch 'feature/property-list-viewmodels' into develop
2 parents cfd574d + 1404be1 commit 3bb1a39

12 files changed

Lines changed: 714 additions & 31 deletions

File tree

app/src/main/java/com/smarttoolfactory/propertyfindar/ui/BottomNavigationFragmentStateAdapter.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,26 @@ class BottomNavigationFragmentStateAdapter(fragmentManager: FragmentManager, lif
2424

2525
override fun createFragment(position: Int): Fragment {
2626
return when (position) {
27+
28+
// Home Dynamic Feature Module
2729
0 -> NavHostContainerFragment.createNavHostContainerFragment(
2830
R.layout.fragment_navhost_home,
2931
R.id.nested_nav_host_fragment_home
3032
)
3133

32-
// Vertical NavHost Post Fragment Container
34+
// Favorites Dynamic Feature Module
3335
1 -> NavHostContainerFragment.createNavHostContainerFragment(
3436
R.layout.fragment_navhost_favorites,
3537
R.id.nested_nav_host_fragment_favorites
3638
)
3739

38-
// Horizontal NavHost Post Fragment Container
40+
// Notification Dynamic Feature Module
3941
2 -> NavHostContainerFragment.createNavHostContainerFragment(
4042
R.layout.fragment_navhost_notification,
4143
R.id.nested_nav_host_fragment_notification
4244
)
4345

46+
// Notification Account Feature Module
4447
else -> NavHostContainerFragment.createNavHostContainerFragment(
4548
R.layout.fragment_navhost_account,
4649
R.id.nested_nav_host_fragment_account
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package com.smarttoolfactory.home.viewbindings
2+
3+
import android.view.View
4+
import android.widget.ImageButton
5+
import android.widget.ImageView
6+
import androidx.databinding.BindingAdapter
7+
import androidx.recyclerview.widget.ListAdapter
8+
import androidx.recyclerview.widget.RecyclerView
9+
import com.bumptech.glide.Glide
10+
import com.bumptech.glide.request.RequestOptions
11+
import com.smarttoolfactory.domain.model.PropertyItem
12+
import com.smarttoolfactory.home.R
13+
14+
/*
15+
*** Bindings for RecyclerView ***
16+
*/
17+
18+
/**
19+
* [BindingAdapter]s for the [PropertyItem]s to ListAdapter.
20+
*/
21+
@BindingAdapter("app:items")
22+
fun RecyclerView.setItems(items: List<PropertyItem>?) {
23+
24+
items?.let {
25+
(adapter as ListAdapter<PropertyItem, *>)?.submitList(items)
26+
}
27+
}
28+
29+
/**
30+
* Binding adapter used with this class android:src used with binding of this object
31+
* loads image from url into specified view
32+
*
33+
* @param view image to be loaded into
34+
* @param path of the image to be fetched
35+
*/
36+
@BindingAdapter("imageSrc")
37+
fun setImageUrl(view: ImageView, path: String?) {
38+
39+
try {
40+
41+
val requestOptions = RequestOptions()
42+
requestOptions.placeholder(R.drawable.placeholder)
43+
44+
Glide
45+
.with(view.context)
46+
.setDefaultRequestOptions(requestOptions)
47+
.load(path)
48+
.into(view)
49+
} catch (e: Exception) {
50+
e.printStackTrace()
51+
}
52+
}
53+
54+
/**
55+
* Display or hide a view based on a condition
56+
*
57+
* @param condition if it's true this View's visibility is set to [View.VISIBLE]
58+
*/
59+
@BindingAdapter("visibilityBasedOn")
60+
fun View.visibilityBasedOn(condition: Boolean) {
61+
visibility = if (condition) View.VISIBLE else View.GONE
62+
}
63+
64+
@BindingAdapter("favoriteImageSrc")
65+
fun ImageButton.setFavoriteImageSrc(favorite: Boolean) {
66+
67+
val imageResource = if (favorite) R.drawable.ic_baseline_favorite_24
68+
else R.drawable.ic_baseline_favorite_24
69+
70+
setImageResource(imageResource)
71+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.smarttoolfactory.home.viewmodel
2+
3+
import androidx.lifecycle.LiveData
4+
import androidx.lifecycle.ViewModel
5+
import com.smarttoolfactory.core.util.Event
6+
import com.smarttoolfactory.core.viewstate.ViewState
7+
import com.smarttoolfactory.domain.model.PropertyItem
8+
9+
/**
10+
* Common class for multiple [ViewModel]s for [PropertyItem]s with Flow, RxJava3, and Pagination
11+
*/
12+
abstract class AbstractPropertyListVM : ViewModel() {
13+
14+
companion object {
15+
const val PROPERTY_LIST = "PROPERTY_LIST"
16+
const val PROPERTY_DETAIL = "PROPERTY_DETAIL"
17+
18+
const val ORDER_BY_NONE = ""
19+
const val ORDER_BY_PRICE_ASCENDING = "pa"
20+
const val ORDER_BY_PRICE_DESCENDING = "pd"
21+
const val ORDER_BY_BEDS_ASCENDING = "ba"
22+
const val ORDER_BY_DES_DESCENDING = "bd"
23+
}
24+
25+
abstract val goToDetailScreen: LiveData<Event<PropertyItem>>
26+
27+
abstract val propertyListViewState: LiveData<ViewState<List<PropertyItem>>>
28+
29+
abstract fun getPropertyList()
30+
31+
abstract fun refreshPropertyList()
32+
33+
abstract fun onClick(propertyItem: PropertyItem)
34+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package com.smarttoolfactory.home.viewmodel
2+
3+
import androidx.hilt.lifecycle.ViewModelInject
4+
import androidx.lifecycle.LiveData
5+
import androidx.lifecycle.MutableLiveData
6+
import com.smarttoolfactory.core.util.Event
7+
import com.smarttoolfactory.core.util.convertToFlowViewState
8+
import com.smarttoolfactory.core.viewstate.Status
9+
import com.smarttoolfactory.core.viewstate.ViewState
10+
import com.smarttoolfactory.domain.model.PropertyItem
11+
import com.smarttoolfactory.domain.usecase.GetPropertiesUseCaseFlow
12+
import kotlinx.coroutines.CoroutineScope
13+
import kotlinx.coroutines.flow.launchIn
14+
import kotlinx.coroutines.flow.onEach
15+
import kotlinx.coroutines.flow.onStart
16+
17+
class PropertyListViewModelFlow @ViewModelInject constructor(
18+
private val coroutineScope: CoroutineScope,
19+
private val getPropertiesUseCase: GetPropertiesUseCaseFlow
20+
) : AbstractPropertyListVM() {
21+
22+
private val _goToDetailScreen = MutableLiveData<Event<PropertyItem>>()
23+
24+
override val goToDetailScreen: LiveData<Event<PropertyItem>>
25+
get() = _goToDetailScreen
26+
27+
private val _propertyViewState = MutableLiveData<ViewState<List<PropertyItem>>>()
28+
29+
override val propertyListViewState: LiveData<ViewState<List<PropertyItem>>>
30+
get() = _propertyViewState
31+
32+
/**
33+
* Function to retrieve data from repository with offline-first which checks
34+
* local data source first.
35+
*
36+
* * Check out Local Source first
37+
* * If empty data or null returned throw empty set exception
38+
* * If error occurred while fetching data from remote: Try to fetch data from db
39+
* * If data is fetched from remote source: delete old data, save new data and return new data
40+
* * If both network and db don't have any data throw empty set exception
41+
*
42+
*/
43+
override fun getPropertyList() {
44+
45+
getPropertiesUseCase.getPropertiesOfflineFirst("")
46+
.convertToFlowViewState()
47+
.onStart {
48+
_propertyViewState.value = ViewState(status = Status.LOADING)
49+
}
50+
.onEach {
51+
_propertyViewState.value = it
52+
}
53+
.launchIn(coroutineScope)
54+
}
55+
56+
override fun refreshPropertyList() {
57+
getPropertiesUseCase.getPropertiesOfflineLast("")
58+
.convertToFlowViewState()
59+
.onStart {
60+
_propertyViewState.value = ViewState(status = Status.LOADING)
61+
}
62+
.onEach {
63+
_propertyViewState.value = it
64+
}
65+
.launchIn(coroutineScope)
66+
}
67+
68+
override fun onClick(item: PropertyItem) {
69+
_goToDetailScreen.value = Event(item)
70+
}
71+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.smarttoolfactory.home.viewmodel
2+
3+
import androidx.hilt.lifecycle.ViewModelInject
4+
import androidx.lifecycle.LiveData
5+
import androidx.lifecycle.MutableLiveData
6+
import com.smarttoolfactory.core.util.Event
7+
import com.smarttoolfactory.core.util.convertFromSingleToObservableViewStateWithLoading
8+
import com.smarttoolfactory.core.viewstate.Status
9+
import com.smarttoolfactory.core.viewstate.ViewState
10+
import com.smarttoolfactory.domain.model.PropertyItem
11+
import com.smarttoolfactory.domain.usecase.GetPropertiesUseCaseRxJava3
12+
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
13+
14+
class PropertyListViewModelRxJava3 @ViewModelInject constructor(
15+
private val getPropertiesUseCase: GetPropertiesUseCaseRxJava3
16+
) : AbstractPropertyListVM() {
17+
18+
private val _goToDetailScreen = MutableLiveData<Event<PropertyItem>>()
19+
20+
override val goToDetailScreen: LiveData<Event<PropertyItem>>
21+
get() = _goToDetailScreen
22+
23+
private val _propertyListViewState = MutableLiveData<ViewState<List<PropertyItem>>>()
24+
25+
override val propertyListViewState: LiveData<ViewState<List<PropertyItem>>>
26+
get() = _propertyListViewState
27+
28+
override fun getPropertyList() {
29+
getPropertiesUseCase.getPropertiesOfflineFirst(ORDER_BY_NONE)
30+
.convertFromSingleToObservableViewStateWithLoading()
31+
.observeOn(AndroidSchedulers.mainThread())
32+
.subscribe(
33+
{
34+
_propertyListViewState.value = it
35+
},
36+
{
37+
_propertyListViewState.value = ViewState(status = Status.ERROR, error = it)
38+
}
39+
)
40+
}
41+
42+
override fun refreshPropertyList() {
43+
getPropertiesUseCase.getPropertiesOfflineLast(ORDER_BY_NONE)
44+
.convertFromSingleToObservableViewStateWithLoading()
45+
.observeOn(AndroidSchedulers.mainThread())
46+
.subscribe(
47+
{
48+
_propertyListViewState.value = it
49+
},
50+
{
51+
_propertyListViewState.value = ViewState(status = Status.ERROR, error = it)
52+
}
53+
)
54+
}
55+
56+
override fun onClick(item: PropertyItem) {
57+
_goToDetailScreen.value = Event(item)
58+
}
59+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.smarttoolfactory.home.viewmodel
2+
3+
import androidx.lifecycle.ViewModel
4+
import androidx.lifecycle.ViewModelProvider
5+
import com.smarttoolfactory.domain.usecase.GetPropertiesUseCaseFlow
6+
import com.smarttoolfactory.domain.usecase.GetPropertiesUseCaseRxJava3
7+
import javax.inject.Inject
8+
import kotlinx.coroutines.CoroutineScope
9+
10+
class PropertyListFlowViewModelFactory @Inject constructor(
11+
private val coroutineScope: CoroutineScope,
12+
private val getPropertiesUseCase: GetPropertiesUseCaseFlow
13+
) : ViewModelProvider.Factory {
14+
15+
@Suppress("UNCHECKED_CAST")
16+
override fun <T : ViewModel> create(modelClass: Class<T>): T {
17+
18+
if (modelClass != PropertyListViewModelFlow::class.java) {
19+
throw IllegalArgumentException("Unknown ViewModel class")
20+
}
21+
22+
return PropertyListViewModelFlow(
23+
coroutineScope,
24+
getPropertiesUseCase
25+
) as T
26+
}
27+
}
28+
29+
class PropertyListRxJava3ViewModelFactory @Inject constructor(
30+
private val getPropertiesUseCase: GetPropertiesUseCaseRxJava3
31+
) : ViewModelProvider.Factory {
32+
33+
@Suppress("UNCHECKED_CAST")
34+
override fun <T : ViewModel> create(modelClass: Class<T>): T {
35+
36+
if (modelClass != PropertyListViewModelRxJava3::class.java) {
37+
throw IllegalArgumentException("Unknown ViewModel class")
38+
}
39+
40+
return PropertyListViewModelRxJava3(getPropertiesUseCase) as T
41+
}
42+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24"
6+
android:tint="?attr/colorControlNormal">
7+
<path
8+
android:fillColor="@android:color/white"
9+
android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/>
10+
</vector>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:viewportWidth="24"
5+
android:viewportHeight="24"
6+
android:tint="?attr/colorControlNormal">
7+
<path
8+
android:fillColor="@android:color/white"
9+
android:pathData="M16.5,3c-1.74,0 -3.41,0.81 -4.5,2.09C10.91,3.81 9.24,3 7.5,3 4.42,3 2,5.42 2,8.5c0,3.78 3.4,6.86 8.55,11.54L12,21.35l1.45,-1.32C18.6,15.36 22,12.28 22,8.5 22,5.42 19.58,3 16.5,3zM12.1,18.55l-0.1,0.1 -0.1,-0.1C7.14,14.24 4,11.39 4,8.5 4,6.5 5.5,5 7.5,5c1.54,0 3.04,0.99 3.57,2.36h1.87C13.46,5.99 14.96,5 16.5,5c2,0 3.5,1.5 3.5,3.5 0,2.89 -3.14,5.74 -7.9,10.05z"/>
10+
</vector>
2.4 KB
Loading

0 commit comments

Comments
 (0)