├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── smarttoolfactory
│ │ └── propertyfindar
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── smarttoolfactory
│ │ │ └── propertyfindar
│ │ │ ├── MainActivity.kt
│ │ │ ├── MainFragment.kt
│ │ │ ├── MainFragmentBottomNav.kt
│ │ │ ├── PropertyFindARApplication.kt
│ │ │ └── ui
│ │ │ └── BottomNavigationFragmentStateAdapter.kt
│ └── res
│ │ ├── anim
│ │ ├── fade_in.xml
│ │ ├── fade_out.xml
│ │ ├── slide_in_left.xml
│ │ ├── slide_in_right.xml
│ │ ├── slide_out_left.xml
│ │ └── slide_out_right.xml
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── asl_bnv_account.xml
│ │ ├── asl_bnv_dashboard.xml
│ │ ├── asl_bnv_home.xml
│ │ ├── asl_bnv_notification.xml
│ │ ├── asl_bnv_settings.xml
│ │ ├── avd_account.xml
│ │ ├── avd_dashboard.xml
│ │ ├── avd_home.xml
│ │ ├── avd_notification.xml
│ │ ├── bottom_navigation_tab_selector.xml
│ │ ├── ic_baseline_account_circle_24.xml
│ │ ├── ic_baseline_favorite_24.xml
│ │ ├── ic_baseline_home_24.xml
│ │ ├── ic_baseline_notifications_24.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_setting_active.xml
│ │ ├── ic_settings_active_avd.xml
│ │ ├── ic_settings_inactive.xml
│ │ ├── vd_account_active.xml
│ │ ├── vd_account_outlined.xml
│ │ ├── vd_dashboard_active.xml
│ │ ├── vd_dashboard_outlined.xml
│ │ ├── vd_home_active.xml
│ │ ├── vd_home_outlined.xml
│ │ ├── vd_notification_active.xml
│ │ └── vd_notification_outlined.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── fragment_main.xml
│ │ ├── fragment_main_bottom_nav.xml
│ │ ├── fragment_navhost_account.xml
│ │ ├── fragment_navhost_dashboard.xml
│ │ ├── fragment_navhost_home.xml
│ │ └── fragment_navhost_notification.xml
│ │ ├── menu
│ │ └── menu_bottom.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── navigation
│ │ ├── nav_graph_dfm_account_start.xml
│ │ ├── nav_graph_dfm_dashboard_start.xml
│ │ ├── nav_graph_dfm_home_start.xml
│ │ ├── nav_graph_dfm_notification_start.xml
│ │ ├── nav_graph_main.xml
│ │ └── nav_graph_main_bottom_nav.xml
│ │ ├── values-night
│ │ └── themes.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ └── test
│ └── java
│ └── com
│ └── smarttoolfactory
│ └── propertyfindar
│ └── ExampleUnitTest.kt
├── build.gradle.kts
├── buildSrc
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ └── java
│ ├── Dependencies.kt
│ ├── Modules.kt
│ ├── Plugins.kt
│ ├── Version.kt
│ └── extension
│ └── DependencyHandlerExtension.kt
├── config
└── detekt
│ └── detekt.yml
├── docs
├── android-final-architecture.png
└── modules.png
├── features
├── account
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── smarttoolfactory
│ │ │ └── account
│ │ │ └── ExampleInstrumentedTest.kt
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── smarttoolfactory
│ │ │ │ └── account
│ │ │ │ └── AccountFragment.kt
│ │ └── res
│ │ │ ├── layout
│ │ │ └── fragment_account.xml
│ │ │ └── navigation
│ │ │ └── nav_graph_account.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── smarttoolfactory
│ │ └── account
│ │ └── ExampleUnitTest.kt
├── dashboard
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── smarttoolfactory
│ │ │ │ └── dashboard
│ │ │ │ ├── Contants.kt
│ │ │ │ ├── DashboardFragment.kt
│ │ │ │ ├── DashboardSeeAllFragment.kt
│ │ │ │ ├── DashboardViewModel.kt
│ │ │ │ ├── DashboardViewModelFactory.kt
│ │ │ │ ├── adapter
│ │ │ │ ├── layoutmanager
│ │ │ │ │ ├── ScaledHorizontalGridLayoutManager.kt
│ │ │ │ │ └── ScaledLinearLayoutManager.kt
│ │ │ │ ├── model
│ │ │ │ │ ├── ChartSectionModel.kt
│ │ │ │ │ ├── Model.kt
│ │ │ │ │ ├── PropertyListModel.kt
│ │ │ │ │ └── RecommendedSectionModel.kt
│ │ │ │ └── viewholder
│ │ │ │ │ ├── BarChartViewBinder.kt
│ │ │ │ │ ├── ChartSectionViewBinder.kt
│ │ │ │ │ ├── CombinedChartViewBinder.kt
│ │ │ │ │ ├── FeaturedSectionViewBinder.kt
│ │ │ │ │ ├── FeaturedVideoViewBinder.kt
│ │ │ │ │ ├── HorizontalPropertyViewBinder.kt
│ │ │ │ │ ├── HorizontalSectionViewBinder.kt
│ │ │ │ │ ├── LoadingViewBinder.kt
│ │ │ │ │ ├── PropertySeeAllListViewBinder.kt
│ │ │ │ │ ├── RecommendedPropertyViewBinder.kt
│ │ │ │ │ └── RecommendedSectionViewBinder.kt
│ │ │ │ ├── di
│ │ │ │ ├── DashboardComponent.kt
│ │ │ │ ├── DashboardModule.kt
│ │ │ │ └── ViewModelFactory.kt
│ │ │ │ └── viewbindings
│ │ │ │ └── DashboardViewBindings.kt
│ │ └── res
│ │ │ ├── drawable
│ │ │ ├── ic_baseline_architecture_24.xml
│ │ │ ├── ic_baseline_bathtub_24.xml
│ │ │ ├── ic_baseline_brightness_low_24.xml
│ │ │ ├── ic_baseline_favorite_30.xml
│ │ │ ├── ic_baseline_favorite_border_24.xml
│ │ │ ├── ic_baseline_favorite_border_30.xml
│ │ │ ├── ic_baseline_hotel_24.xml
│ │ │ ├── ic_baseline_remove_red_eye_24.xml
│ │ │ ├── ic_baseline_sort_24.xml
│ │ │ └── placeholder.png
│ │ │ ├── layout
│ │ │ ├── fragment_bar_chart.xml
│ │ │ ├── fragment_dashboard.xml
│ │ │ ├── fragment_dashboard_see_all.xml
│ │ │ ├── item_chart_bar.xml
│ │ │ ├── item_chart_section.xml
│ │ │ ├── item_chart_trends.xml
│ │ │ ├── item_loading.xml
│ │ │ ├── item_property_horizontal.xml
│ │ │ ├── item_property_section_horizontal.xml
│ │ │ ├── item_property_see_all.xml
│ │ │ ├── item_recommended_property.xml
│ │ │ └── item_recommended_section.xml
│ │ │ └── navigation
│ │ │ └── nav_graph_dashboard.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── smarttoolfactory
│ │ └── dashboard
│ │ └── ExampleUnitTest.kt
├── home
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── smarttoolfactory
│ │ │ └── home
│ │ │ └── ExampleInstrumentedTest.kt
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── smarttoolfactory
│ │ │ │ └── home
│ │ │ │ ├── HomeFragment.kt
│ │ │ │ ├── adapter
│ │ │ │ ├── HomeFragmentStateAdapter.kt
│ │ │ │ ├── LoadingAdapter.kt
│ │ │ │ ├── model
│ │ │ │ │ └── PropertyListModel.kt
│ │ │ │ └── viewholder
│ │ │ │ │ └── PropertyListViewBinder.kt
│ │ │ │ ├── di
│ │ │ │ ├── HomeComponent.kt
│ │ │ │ └── HomeModule.kt
│ │ │ │ ├── propertylist
│ │ │ │ ├── AbstractPropertyListVM.kt
│ │ │ │ ├── flow
│ │ │ │ │ ├── PropertyListFragment.kt
│ │ │ │ │ └── PropertyListViewModel.kt
│ │ │ │ ├── paged
│ │ │ │ │ ├── PagedPropertyListFragment.kt
│ │ │ │ │ └── PagedPropertyListViewModel.kt
│ │ │ │ └── rxjava
│ │ │ │ │ ├── PropertyListFragmentRxJava3.kt
│ │ │ │ │ └── PropertyListViewModelRxJava3.kt
│ │ │ │ ├── viewbindings
│ │ │ │ └── HomeViewBindings.kt
│ │ │ │ └── viewmodel
│ │ │ │ ├── HomeToolbarVM.kt
│ │ │ │ └── ViewModelFactory.kt
│ │ └── res
│ │ │ ├── anim
│ │ │ ├── fade_in.xml
│ │ │ ├── fade_out.xml
│ │ │ ├── slide_in_left.xml
│ │ │ ├── slide_in_right.xml
│ │ │ ├── slide_out_left.xml
│ │ │ └── slide_out_right.xml
│ │ │ ├── drawable
│ │ │ ├── asl_heart_break.xml
│ │ │ ├── avd_heart_empty_to_filled.xml
│ │ │ ├── avd_heart_filled_to_broken.xml
│ │ │ ├── ic_baseline_architecture_24.xml
│ │ │ ├── ic_baseline_favorite_24.xml
│ │ │ ├── ic_baseline_favorite_30.xml
│ │ │ ├── ic_baseline_favorite_border_24.xml
│ │ │ ├── ic_baseline_favorite_border_30.xml
│ │ │ ├── ic_baseline_hotel_24.xml
│ │ │ ├── ic_baseline_remove_red_eye_24.xml
│ │ │ ├── ic_baseline_sort_24.xml
│ │ │ ├── ic_outline_airline_seat_individual_suite_24.xml
│ │ │ ├── placeholder.png
│ │ │ ├── vd_heart_empty.xml
│ │ │ └── vd_heart_filled.xml
│ │ │ ├── layout
│ │ │ ├── fragment_home.xml
│ │ │ ├── fragment_navhost_property_list_flow.xml
│ │ │ ├── fragment_navhost_property_list_paged.xml
│ │ │ ├── fragment_navhost_property_list_rxjava3.xml
│ │ │ ├── fragment_property_list.xml
│ │ │ ├── fragment_property_list_paged.xml
│ │ │ └── item_property_list.xml
│ │ │ ├── menu
│ │ │ └── menu_home.xml
│ │ │ └── navigation
│ │ │ ├── nav_graph_home.xml
│ │ │ ├── nav_graph_property_list_flow.xml
│ │ │ ├── nav_graph_property_list_paged.xml
│ │ │ └── nav_graph_property_list_rxjava3.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── smarttoolfactory
│ │ └── home
│ │ ├── ExampleUnitTest.kt
│ │ └── viewmodel
│ │ ├── PropertyListViewModelFlowTest.kt
│ │ └── PropertyListViewModelRxJava3Test.kt
├── notification
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── smarttoolfactory
│ │ │ └── notification
│ │ │ └── ExampleInstrumentedTest.kt
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── smarttoolfactory
│ │ │ │ └── notification
│ │ │ │ └── NotificationFragment.kt
│ │ └── res
│ │ │ ├── layout
│ │ │ └── fragment_notification.xml
│ │ │ └── navigation
│ │ │ └── nav_graph_notification.xml
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── smarttoolfactory
│ │ └── notification
│ │ └── ExampleUnitTest.kt
└── property_detail
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── smarttoolfactory
│ │ └── property_detail
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── smarttoolfactory
│ │ │ └── property_detail
│ │ │ ├── DetailViewBindings.kt
│ │ │ ├── PropertyDetailFragment.kt
│ │ │ ├── PropertyDetailViewModel.kt
│ │ │ └── di
│ │ │ ├── PropertyDetailComponent.kt
│ │ │ └── PropertyDetailModule.kt
│ └── res
│ │ ├── drawable
│ │ └── placeholder.png
│ │ ├── layout
│ │ ├── fragment_property_detail.xml
│ │ └── fragment_property_detail_plain.xml
│ │ └── navigation
│ │ └── nav_graph_property_detail.xml
│ └── test
│ └── java
│ └── com
│ └── smarttoolfactory
│ └── property_detail
│ └── ExampleUnitTest.kt
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── libraries
├── core
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── smarttoolfactory
│ │ │ └── core
│ │ │ └── ExampleInstrumentedTest.kt
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── assets
│ │ │ ├── construction_process.json
│ │ │ ├── home.json
│ │ │ └── under_construction.json
│ │ └── java
│ │ │ └── com
│ │ │ └── smarttoolfactory
│ │ │ └── core
│ │ │ ├── di
│ │ │ ├── CoreModule.kt
│ │ │ ├── CoreModuleDependencies.kt
│ │ │ ├── DataModule.kt
│ │ │ ├── qualifier
│ │ │ │ └── RecycledViewPool.kt
│ │ │ └── scope
│ │ │ │ └── FeatureScope.kt
│ │ │ ├── error
│ │ │ └── NavigationException.kt
│ │ │ ├── ui
│ │ │ ├── fragment
│ │ │ │ ├── BaseDataBindingFragment.kt
│ │ │ │ ├── DynamicNavigationFragment.kt
│ │ │ │ └── navhost
│ │ │ │ │ ├── BaseDynamicNavHostFragment.kt
│ │ │ │ │ ├── BaseNavHostFragment.kt
│ │ │ │ │ └── NavHostContainerFragment.kt
│ │ │ ├── recyclerview
│ │ │ │ ├── adapter
│ │ │ │ │ ├── ItemDiffCallback.kt
│ │ │ │ │ ├── MultipleViewBinderListAdapter.kt
│ │ │ │ │ ├── SingleLayoutListAdapter.kt
│ │ │ │ │ ├── SingleViewBinderListAdapter.kt
│ │ │ │ │ └── ViewBinders.kt
│ │ │ │ └── itemcallback
│ │ │ │ │ ├── DefaultItemCallback.kt
│ │ │ │ │ └── PropertyItemCallback.kt
│ │ │ ├── viewpager2
│ │ │ │ └── NavigableFragmentStateAdapter.kt
│ │ │ └── widget
│ │ │ │ └── NestedScrollableHost.kt
│ │ │ ├── util
│ │ │ ├── DynamicNavigationExtension.kt
│ │ │ ├── EndlessScrollListener.kt
│ │ │ ├── Event.kt
│ │ │ ├── FlowViewStateExtension.kt
│ │ │ ├── LifecycleOwnerExtension.kt
│ │ │ ├── NavHostExtension.kt
│ │ │ ├── RxJavaViewStateExtension.kt
│ │ │ ├── ViewBindings.kt
│ │ │ └── ViewExtension.kt
│ │ │ ├── viewmodel
│ │ │ ├── NavControllerViewModel.kt
│ │ │ └── PropertyDetailNavigationVM.kt
│ │ │ └── viewstate
│ │ │ └── ViewState.kt
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── smarttoolfactory
│ │ └── core
│ │ └── ExampleUnitTest.kt
├── data
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ ├── schemas
│ │ └── com.smarttoolfactory.data.db.PropertyDatabase
│ │ │ ├── 1.json
│ │ │ ├── 2.json
│ │ │ ├── 3.json
│ │ │ └── 4.json
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── com
│ │ │ └── smarttoolfactory
│ │ │ └── data
│ │ │ ├── AbstractDaoTest.kt
│ │ │ ├── FavoriteDaoTest.kt
│ │ │ ├── PropertyDaoCoroutinesTest.kt
│ │ │ ├── PropertyDaoRxJavaTest.kt
│ │ │ ├── PropertyDaoTestSuite.kt
│ │ │ └── UserDaoTest.kt
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── smarttoolfactory
│ │ │ └── data
│ │ │ ├── api
│ │ │ └── PropertyApi.kt
│ │ │ ├── constant
│ │ │ └── Constants.kt
│ │ │ ├── db
│ │ │ ├── PropertyDatabase.kt
│ │ │ ├── converters
│ │ │ │ └── PropertyTypeConverters.kt
│ │ │ └── dao
│ │ │ │ ├── BaseDao.kt
│ │ │ │ ├── FavoritesCoroutinesDao.kt
│ │ │ │ ├── FavoritesRxJava3Dao.kt
│ │ │ │ ├── PagedPropertyDao.kt
│ │ │ │ ├── PropertyCoroutinesDao.kt
│ │ │ │ ├── PropertyRxJava3Dao.kt
│ │ │ │ ├── SortOrderDAOs.kt
│ │ │ │ └── UserDao.kt
│ │ │ ├── di
│ │ │ ├── DatabaseModule.kt
│ │ │ └── NetworkModule.kt
│ │ │ ├── mapper
│ │ │ └── MappingFactory.kt
│ │ │ ├── model
│ │ │ ├── Mappables.kt
│ │ │ ├── local
│ │ │ │ ├── BasePropertyEntity.kt
│ │ │ │ ├── BrokerEntity.kt
│ │ │ │ ├── InteractivePropertyEntity.kt
│ │ │ │ ├── PagedPropertyEntity.kt
│ │ │ │ ├── PropertyEntity.kt
│ │ │ │ ├── PropertyStatus.kt
│ │ │ │ ├── SortOrderEntity.kt
│ │ │ │ ├── UserEntity.kt
│ │ │ │ └── UserFavoriteJoin.kt
│ │ │ └── remote
│ │ │ │ ├── BrokerDTO.kt
│ │ │ │ ├── PropertyDTO.kt
│ │ │ │ └── PropertyResponse.kt
│ │ │ ├── repository
│ │ │ ├── FavoritesRepoImpl.kt
│ │ │ ├── FavoritesRepository.kt
│ │ │ ├── PropertyRepoImpl.kt
│ │ │ └── PropertyRepository.kt
│ │ │ └── source
│ │ │ ├── FavoritePropertyDataSource.kt
│ │ │ ├── FavoritePropertyDataSourceRxJava3.kt
│ │ │ ├── PropertyDataSource.kt
│ │ │ └── PropertyDataSourceImpl.kt
│ │ └── test
│ │ └── java
│ │ └── com
│ │ └── smarttoolfactory
│ │ └── data
│ │ ├── api
│ │ ├── AbstractPropertyApiTest.kt
│ │ ├── PropertyApiCoroutinesTest.kt
│ │ └── PropertyApiRxJava3Test.kt
│ │ ├── mapper
│ │ └── PropertyEntityEntityListMapperTest.kt
│ │ ├── repository
│ │ ├── PagedPropertyRepositoryImplTest.kt
│ │ ├── PropertyRepositoryCoroutinesTest.kt
│ │ └── PropertyRepositoryRxJava3Test.kt
│ │ └── source
│ │ ├── PropertyDataSourceCoroutinesTest.kt
│ │ └── PropertyDataSourceRxJava3Test.kt
├── domain
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── smarttoolfactory
│ │ │ └── domain
│ │ │ ├── Constants.kt
│ │ │ ├── base
│ │ │ └── Disposable.kt
│ │ │ ├── dispatcher
│ │ │ └── UseCaseDispatchers.kt
│ │ │ ├── error
│ │ │ └── EmptyDataException.kt
│ │ │ ├── mapper
│ │ │ └── Mappers.kt
│ │ │ ├── model
│ │ │ ├── BrokerItem.kt
│ │ │ ├── Item.kt
│ │ │ ├── PropertyChartItem.kt
│ │ │ └── PropertyItem.kt
│ │ │ └── usecase
│ │ │ └── property
│ │ │ ├── GetDashboardStatsUseCase.kt
│ │ │ ├── GetPropertiesUseCaseFlow.kt
│ │ │ ├── GetPropertiesUseCasePaged.kt
│ │ │ ├── GetPropertiesUseCaseRxJava3.kt
│ │ │ ├── SetPropertyStatsUseCase.kt
│ │ │ └── SetPropertyStatsUseCaseRxJava3.kt
│ │ └── test
│ │ ├── java
│ │ └── com
│ │ │ └── smarttoolfactory
│ │ │ └── domain
│ │ │ ├── ExampleUnitTest.kt
│ │ │ ├── mapper
│ │ │ └── PropertyEntityToItemListMapperTest.kt
│ │ │ └── usecase
│ │ │ ├── GetPropertiesUseCaseFlowTest.kt
│ │ │ ├── GetPropertiesUseCaseRxJava3Test.kt
│ │ │ └── TestData.kt
│ │ └── resources
│ │ ├── response.json
│ │ ├── response_page1.json
│ │ ├── response_page2.json
│ │ ├── response_page3.json
│ │ ├── response_sort_by_ba.json
│ │ ├── response_sort_by_bd.json
│ │ ├── response_sort_by_none.json
│ │ ├── response_sort_by_pa.json
│ │ └── response_sort_by_pd.json
└── test-utils
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── smarttoolfactory
│ │ └── test_utils
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── smarttoolfactory
│ │ └── test_utils
│ │ ├── TestConstans.kt
│ │ ├── extension
│ │ ├── RxImmediateSchedulerExtension.kt
│ │ └── TestCoroutineExtension.kt
│ │ ├── rule
│ │ ├── MockWebServerRule.kt
│ │ ├── RxImmediateSchedulerRule.kt
│ │ └── TestCoroutineRule.kt
│ │ ├── test_observer
│ │ ├── FlowTestObserver.kt
│ │ └── LiveDataTestObserver.kt
│ │ └── util
│ │ ├── LiveDataTestUtil.kt
│ │ └── ReadResourceUtil.kt
│ └── test
│ ├── java
│ └── com
│ │ └── smarttoolfactory
│ │ └── test_utils
│ │ └── ExampleUnitTest.kt
│ └── resources
│ ├── response.json
│ ├── response_page1.json
│ ├── response_page2.json
│ ├── response_page3.json
│ ├── response_sort_by_ba.json
│ ├── response_sort_by_bd.json
│ ├── response_sort_by_none.json
│ ├── response_sort_by_pa.json
│ └── response_sort_by_pd.json
├── screenshots
├── account.png
├── dashboard.png
├── notifications.png
├── property_flow.gif
├── property_overview.gif
├── property_pagination.gif
└── property_rxjava3.gif
├── scripts
└── git-hooks
│ └── pre-commit
└── settings.gradle.kts
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/*
5 | /.idea/caches
6 | /.idea/libraries
7 | /.idea/modules.xml
8 | /.idea/workspace.xml
9 | /.idea/navEditor.xml
10 | /.idea/assetWizardSettings.xml
11 | .DS_Store
12 | /build
13 | /captures
14 | .externalNativeBuild
15 | .cxx
16 | local.properties
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/smarttoolfactory/propertyfindar/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.propertyfindar
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumented test, which will execute on an Android device.
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | fun useAppContext() {
18 | // Context of the app under test.
19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20 | assertEquals("com.smarttoolfactory.propertyfindar", appContext.packageName)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/smarttoolfactory/propertyfindar/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.propertyfindar
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import dagger.hilt.android.AndroidEntryPoint
6 |
7 | @AndroidEntryPoint
8 | class MainActivity : AppCompatActivity() {
9 | override fun onCreate(savedInstanceState: Bundle?) {
10 | super.onCreate(savedInstanceState)
11 | setContentView(R.layout.activity_main)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/smarttoolfactory/propertyfindar/PropertyFindARApplication.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.propertyfindar
2 |
3 | import android.app.Application
4 | import dagger.hilt.android.HiltAndroidApp
5 |
6 | @HiltAndroidApp
7 | class PropertyFindARApplication : Application()
8 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/fade_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/fade_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/slide_in_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/slide_in_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/slide_out_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/slide_out_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/asl_bnv_account.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
13 |
14 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/asl_bnv_dashboard.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
12 |
13 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/asl_bnv_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
12 |
13 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/asl_bnv_notification.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
13 |
14 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/asl_bnv_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
11 |
12 |
13 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/avd_home.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
11 |
15 |
20 |
21 |
22 |
26 |
27 |
31 |
32 |
33 |
34 |
35 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bottom_navigation_tab_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_account_circle_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_favorite_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_home_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_notifications_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vd_account_active.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vd_account_outlined.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vd_dashboard_active.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vd_dashboard_outlined.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vd_home_active.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vd_home_outlined.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vd_notification_active.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/vd_notification_outlined.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
18 |
19 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_main_bottom_nav.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
17 |
18 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_navhost_account.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_navhost_dashboard.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_navhost_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_navhost_notification.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/PropertyFindAR/5d2b510a8d1c489a5520b1fff6a143552e50ca79/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/PropertyFindAR/5d2b510a8d1c489a5520b1fff6a143552e50ca79/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/PropertyFindAR/5d2b510a8d1c489a5520b1fff6a143552e50ca79/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/PropertyFindAR/5d2b510a8d1c489a5520b1fff6a143552e50ca79/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/PropertyFindAR/5d2b510a8d1c489a5520b1fff6a143552e50ca79/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/PropertyFindAR/5d2b510a8d1c489a5520b1fff6a143552e50ca79/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/PropertyFindAR/5d2b510a8d1c489a5520b1fff6a143552e50ca79/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/PropertyFindAR/5d2b510a8d1c489a5520b1fff6a143552e50ca79/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/PropertyFindAR/5d2b510a8d1c489a5520b1fff6a143552e50ca79/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/PropertyFindAR/5d2b510a8d1c489a5520b1fff6a143552e50ca79/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/navigation/nav_graph_dfm_account_start.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/navigation/nav_graph_dfm_dashboard_start.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/navigation/nav_graph_dfm_home_start.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/navigation/nav_graph_dfm_notification_start.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/navigation/nav_graph_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
26 |
31 |
32 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/navigation/nav_graph_main_bottom_nav.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
14 |
15 |
22 |
23 |
24 |
25 |
26 |
27 |
32 |
33 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 | #F5F5F5
11 | #9E9E9E
12 | #E0E0E0
13 | #F57C00
14 | #ff757575
15 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Property FindAR
3 | Home
4 | Dashboard
5 | Notification
6 | Account
7 | Search
8 | Property Detail
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
21 |
--------------------------------------------------------------------------------
/app/src/test/java/com/smarttoolfactory/propertyfindar/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.propertyfindar
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/buildSrc/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | /build
3 |
--------------------------------------------------------------------------------
/buildSrc/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import org.gradle.kotlin.dsl.`kotlin-dsl`
2 |
3 | plugins {
4 | `kotlin-dsl`
5 | }
6 |
7 | repositories {
8 | jcenter()
9 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/java/Modules.kt:
--------------------------------------------------------------------------------
1 | object Modules {
2 |
3 | const val APP = ":app"
4 |
5 | object AndroidLibrary {
6 | const val CORE = ":libraries:core"
7 | const val DATA = ":libraries:data"
8 | const val DOMAIN = ":libraries:domain"
9 | const val TEST_UTILS = ":libraries:test-utils"
10 | }
11 |
12 | /**
13 | * Dynamic Feature Modules
14 | */
15 | object DynamicFeature {
16 | const val HOME = ":features:home"
17 | const val PROPERTY_DETAIL = ":features:property_detail"
18 | const val DASHBOARD = ":features:dashboard"
19 | const val NOTIFICATION = ":features:notification"
20 | const val ACCOUNT = ":features:account"
21 | const val SEARCH = ":features:search"
22 | }
23 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/java/Plugins.kt:
--------------------------------------------------------------------------------
1 | object Plugins {
2 |
3 | /*
4 | Project Level
5 | */
6 | const val GRADLE = "com.android.tools.build:gradle"
7 | const val DETEKT = "io.gitlab.arturbosch.detekt"
8 | const val KTLINT = "org.jlleitschuh.gradle.ktlint"
9 | const val GIT_HOOKS = "plugins.git-hooks"
10 |
11 | const val CLASSPATH_GRADLE = "com.android.tools.build:gradle:${PluginVersion.GRADLE_VERSION}"
12 | const val CLASSPATH_KTLINT =
13 | "org.jlleitschuh.gradle:ktlint-gradle:${PluginVersion.KTLINT_VERSION}"
14 | const val CLASSPATH_DAGGER_HILT =
15 | "com.google.dagger:hilt-android-gradle-plugin:${Version.DAGGER_VERSION}"
16 | const val CLASSPATH_NAV_SAFE_ARGS =
17 | "androidx.navigation:navigation-safe-args-gradle-plugin:${PluginVersion.NAV_SAFE_ARGS_VERSION}"
18 |
19 | const val CLASSPATH_MP_CHART = "com.github.dcendents:android-maven-gradle-plugin:2.1"
20 |
21 | /*
22 | Module Level
23 | */
24 | const val DAGGER_HILT_PLUGIN = "dagger.hilt.android.plugin"
25 | const val ANDROID_APPLICATION_PLUGIN = "com.android.application"
26 | const val ANDROID_DYNAMIC_FEATURE_PLUGIN = "com.android.dynamic-feature"
27 | const val ANDROID_LIBRARY_PLUGIN = "com.android.library"
28 |
29 | const val KOTLIN_ANDROID_PLUGIN = "kotlin-android"
30 | const val KOTLIN_ANDROID_EXTENSIONS_PLUGIN = "kotlin-android-extensions"
31 | const val KOTLIN_KAPT_PLUGIN = "kotlin-kapt"
32 | const val NAVIGATION_SAFE_ARGS = "androidx.navigation.safeargs.kotlin"
33 | }
34 |
--------------------------------------------------------------------------------
/docs/android-final-architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/PropertyFindAR/5d2b510a8d1c489a5520b1fff6a143552e50ca79/docs/android-final-architecture.png
--------------------------------------------------------------------------------
/docs/modules.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/PropertyFindAR/5d2b510a8d1c489a5520b1fff6a143552e50ca79/docs/modules.png
--------------------------------------------------------------------------------
/features/account/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/features/account/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import extension.addBaseDynamicFeatureModuleDependencies
2 | import extension.addInstrumentationTestDependencies
3 | import extension.addUnitTestDependencies
4 |
5 | plugins {
6 | id(Plugins.ANDROID_DYNAMIC_FEATURE_PLUGIN)
7 | id(Plugins.KOTLIN_ANDROID_PLUGIN)
8 | id(Plugins.KOTLIN_ANDROID_EXTENSIONS_PLUGIN)
9 | id(Plugins.KOTLIN_KAPT_PLUGIN)
10 | id(Plugins.DAGGER_HILT_PLUGIN)
11 | }
12 |
13 | android {
14 |
15 | compileSdkVersion(AndroidVersion.COMPILE_SDK_VERSION)
16 |
17 | defaultConfig {
18 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
19 | }
20 |
21 | buildTypes {
22 | getByName("release") {
23 | isMinifyEnabled = false
24 | proguardFiles(
25 | getDefaultProguardFile("proguard-android-optimize.txt"),
26 | "proguard-rules.pro"
27 | )
28 | }
29 | }
30 |
31 | packagingOptions {
32 | exclude("META-INF/AL2.0")
33 | }
34 |
35 | dataBinding.isEnabled = true
36 | // android.buildFeatures.viewBinding = true
37 |
38 | compileOptions {
39 | sourceCompatibility = JavaVersion.VERSION_1_8
40 | targetCompatibility = JavaVersion.VERSION_1_8
41 | }
42 | kotlinOptions {
43 | jvmTarget = "1.8"
44 | }
45 | }
46 |
47 | dependencies {
48 |
49 | implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
50 |
51 | implementation(project(Modules.APP))
52 | implementation(project(Modules.AndroidLibrary.CORE))
53 | implementation(project(Modules.AndroidLibrary.DOMAIN))
54 |
55 | addBaseDynamicFeatureModuleDependencies()
56 |
57 | // Support and Widgets
58 | implementation(Deps.APPCOMPAT)
59 | implementation(Deps.MATERIAL)
60 | implementation(Deps.CONSTRAINT_LAYOUT)
61 |
62 | // Glide
63 | implementation(Deps.GLIDE)
64 | kapt(Deps.GLIDE_COMPILER)
65 |
66 | // Lottie
67 | implementation(Deps.LOTTIE)
68 |
69 | // Unit Tests
70 | addUnitTestDependencies()
71 | testImplementation(project(Modules.AndroidLibrary.TEST_UTILS))
72 |
73 | // Instrumentation Tests
74 | addInstrumentationTestDependencies()
75 | androidTestImplementation(project(Modules.AndroidLibrary.TEST_UTILS))
76 | }
77 |
--------------------------------------------------------------------------------
/features/account/src/androidTest/java/com/smarttoolfactory/account/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.account
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumented test, which will execute on an Android device.
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | fun useAppContext() {
18 | // Context of the app under test.
19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20 | assertEquals("com.smarttoolfactory.account", appContext.packageName)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/features/account/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/features/account/src/main/java/com/smarttoolfactory/account/AccountFragment.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.account
2 |
3 | import com.smarttoolfactory.account.databinding.FragmentAccountBinding
4 | import com.smarttoolfactory.core.ui.fragment.DynamicNavigationFragment
5 |
6 | class AccountFragment : DynamicNavigationFragment() {
7 |
8 | override fun getLayoutRes(): Int = R.layout.fragment_account
9 | }
10 |
--------------------------------------------------------------------------------
/features/account/src/main/res/layout/fragment_account.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 |
20 |
21 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/features/account/src/main/res/navigation/nav_graph_account.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/features/account/src/test/java/com/smarttoolfactory/account/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.account
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/features/dashboard/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/features/dashboard/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/java/com/smarttoolfactory/dashboard/Contants.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.dashboard
2 |
3 | const val KEY_FAVORITES_LAYOUT_MANAGER_STATE =
4 | "favorites_layout_manager"
5 | const val KEY_MOST_VIEWED_LAYOUT_MANAGER_STATE =
6 | "views_layout_manager"
7 | const val KEY_RECOMMENDED_LAYOUT_MANAGER_STATE =
8 | "recommended_layout_manager"
9 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/java/com/smarttoolfactory/dashboard/DashboardViewModelFactory.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.dashboard
2 |
3 | import androidx.lifecycle.SavedStateHandle
4 | import com.smarttoolfactory.dashboard.di.ViewModelFactory
5 | import com.smarttoolfactory.domain.usecase.property.GetDashboardStatsUseCase
6 | import com.smarttoolfactory.domain.usecase.property.SetPropertyStatsUseCase
7 | import javax.inject.Inject
8 | import kotlinx.coroutines.CoroutineScope
9 |
10 | class DashboardViewModelFactory @Inject constructor(
11 | private val coroutineScope: CoroutineScope,
12 | private val dashboardStatsUseCase: GetDashboardStatsUseCase,
13 | private val setPropertyStatsUseCase: SetPropertyStatsUseCase
14 | ) : ViewModelFactory {
15 |
16 | override fun create(handle: SavedStateHandle): DashboardViewModel {
17 | return DashboardViewModel(
18 | handle,
19 | coroutineScope,
20 | dashboardStatsUseCase,
21 | setPropertyStatsUseCase
22 | )
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/java/com/smarttoolfactory/dashboard/adapter/layoutmanager/ScaledLinearLayoutManager.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.dashboard.adapter.layoutmanager
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.ViewGroup
6 | import androidx.recyclerview.widget.LinearLayoutManager
7 | import androidx.recyclerview.widget.RecyclerView
8 |
9 | /**
10 | * Layout manager for displaying last item visible partially after total item set
11 | */
12 | class ScaledLinearLayoutManager constructor(
13 | context: Context?,
14 | @RecyclerView.Orientation orientation: Int = RecyclerView.HORIZONTAL,
15 | reverseLayout: Boolean = false,
16 | private val totalItems: Int = 1,
17 | private val partialVisibilityRatio: Float = 0.6f
18 | ) : LinearLayoutManager(context, orientation, reverseLayout) {
19 |
20 | private val horizontalSpace get() = width - paddingStart - paddingEnd
21 |
22 | override fun generateDefaultLayoutParams() =
23 | scaledLayoutParams(super.generateDefaultLayoutParams())
24 |
25 | override fun generateLayoutParams(lp: ViewGroup.LayoutParams?) =
26 | scaledLayoutParams(super.generateLayoutParams(lp))
27 |
28 | override fun generateLayoutParams(c: Context?, attrs: AttributeSet?) =
29 | scaledLayoutParams(super.generateLayoutParams(c, attrs))
30 |
31 | private fun scaledLayoutParams(layoutParams: RecyclerView.LayoutParams) =
32 | layoutParams.apply {
33 | val marginBetweenItems =
34 | (layoutParams.marginStart + layoutParams.marginEnd) * itemCount
35 |
36 | val oneItemRatio = ((100 / (totalItems.toFloat() + partialVisibilityRatio)) / 100)
37 | width = (oneItemRatio * (horizontalSpace - 0)).toInt()
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/java/com/smarttoolfactory/dashboard/adapter/model/ChartSectionModel.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.dashboard.adapter.model
2 |
3 | import com.smarttoolfactory.domain.model.PropertyChartItem
4 |
5 | data class ChartSectionModel(
6 | val items: List,
7 | val chartTitle: String = ""
8 | ) : Model
9 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/java/com/smarttoolfactory/dashboard/adapter/model/Model.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.dashboard.adapter.model
2 |
3 | import com.smarttoolfactory.core.ui.recyclerview.adapter.MappableItemViewBinder
4 |
5 | /**
6 | * Marker for RecyclerView layout models for binding single model to, single layout with
7 | * [MappableItemViewBinder]
8 | */
9 | interface Model
10 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/java/com/smarttoolfactory/dashboard/adapter/model/PropertyListModel.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.dashboard.adapter.model
2 |
3 | import android.os.Parcelable
4 | import com.smarttoolfactory.domain.model.PropertyItem
5 | import kotlinx.android.parcel.Parcelize
6 |
7 | /**
8 | * Model for list that has a title,
9 | * and data for horizontally scrollable items in RecyclerView
10 | */
11 | @Parcelize
12 | data class PropertyListModel(
13 | val title: String = "",
14 | val items: List,
15 | var seeAll: Boolean = true
16 | ) : Model, Parcelable
17 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/java/com/smarttoolfactory/dashboard/adapter/model/RecommendedSectionModel.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.dashboard.adapter.model
2 |
3 | import com.smarttoolfactory.domain.model.PropertyItem
4 |
5 | data class RecommendedSectionModel(
6 | val title: String = "",
7 | val items: List
8 | ) : Model
9 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/java/com/smarttoolfactory/dashboard/adapter/viewholder/CombinedChartViewBinder.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.dashboard.adapter.viewholder
2 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/java/com/smarttoolfactory/dashboard/adapter/viewholder/FeaturedSectionViewBinder.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.dashboard.adapter.viewholder
2 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/java/com/smarttoolfactory/dashboard/adapter/viewholder/FeaturedVideoViewBinder.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.dashboard.adapter.viewholder
2 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/java/com/smarttoolfactory/dashboard/adapter/viewholder/LoadingViewBinder.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.dashboard.adapter.viewholder
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import androidx.recyclerview.widget.RecyclerView
7 | import com.smarttoolfactory.core.ui.recyclerview.adapter.MappableItemViewBinder
8 | import com.smarttoolfactory.dashboard.R
9 |
10 | // Shown while loading
11 | object LoadingIndicator
12 |
13 | class LoadingViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
14 |
15 | class LoadingViewBinder : MappableItemViewBinder(
16 | LoadingIndicator::class.java
17 | ) {
18 |
19 | override fun createViewHolder(parent: ViewGroup): RecyclerView.ViewHolder {
20 | return LoadingViewHolder(
21 | LayoutInflater.from(parent.context).inflate(getItemLayoutResource(), parent, false)
22 | )
23 | }
24 |
25 | override fun bindViewHolder(model: LoadingIndicator, viewHolder: LoadingViewHolder) = Unit
26 |
27 | override fun getItemLayoutResource() = R.layout.item_loading
28 |
29 | override fun areItemsTheSame(oldItem: LoadingIndicator, newItem: LoadingIndicator) = true
30 |
31 | override fun areContentsTheSame(oldItem: LoadingIndicator, newItem: LoadingIndicator) = true
32 | }
33 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/java/com/smarttoolfactory/dashboard/di/DashboardComponent.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.dashboard.di
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.smarttoolfactory.core.di.CoreModuleDependencies
5 | import com.smarttoolfactory.dashboard.DashboardFragment
6 | import dagger.BindsInstance
7 | import dagger.Component
8 |
9 | @Component(
10 | dependencies = [CoreModuleDependencies::class],
11 | modules = [DashboardModule::class]
12 | )
13 | interface DashboardComponent {
14 |
15 | fun inject(fragment: DashboardFragment)
16 |
17 | @Component.Factory
18 | interface Factory {
19 | fun create(
20 | dependentModule: CoreModuleDependencies,
21 | @BindsInstance fragment: Fragment
22 | ): DashboardComponent
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/java/com/smarttoolfactory/dashboard/di/DashboardModule.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.dashboard.di
2 |
3 | import androidx.recyclerview.widget.RecyclerView
4 | import com.smarttoolfactory.core.di.qualifier.RecycledViewPool
5 | import dagger.Module
6 | import dagger.Provides
7 | import dagger.hilt.InstallIn
8 | import dagger.hilt.android.components.FragmentComponent
9 | import kotlinx.coroutines.CoroutineScope
10 | import kotlinx.coroutines.Dispatchers
11 | import kotlinx.coroutines.SupervisorJob
12 |
13 | @InstallIn(FragmentComponent::class)
14 | @Module
15 | class DashboardModule {
16 |
17 | @Provides
18 | fun provideCoroutineScope() = CoroutineScope(Dispatchers.Main.immediate + SupervisorJob())
19 |
20 | /*
21 | Shared RecycledViewPool to lower number of inflation counts in inner RecyclerViews
22 | that use same Views
23 | */
24 | @RecycledViewPool(value = RecycledViewPool.Type.PROPERTY_HORIZONTAL)
25 | @Provides
26 | fun provideHorizontalRecycledViewPool() = RecyclerView.RecycledViewPool()
27 |
28 | @RecycledViewPool(value = RecycledViewPool.Type.CHART_ITEM)
29 | @Provides
30 | fun provideChartItemRecycledViewPool() = RecyclerView.RecycledViewPool()
31 | }
32 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/java/com/smarttoolfactory/dashboard/di/ViewModelFactory.kt:
--------------------------------------------------------------------------------
1 |
2 | package com.smarttoolfactory.dashboard.di
3 |
4 | import android.os.Bundle
5 | import androidx.annotation.MainThread
6 | import androidx.lifecycle.AbstractSavedStateViewModelFactory
7 | import androidx.lifecycle.SavedStateHandle
8 | import androidx.lifecycle.ViewModel
9 | import androidx.savedstate.SavedStateRegistryOwner
10 |
11 | interface ViewModelFactory {
12 | fun create(handle: SavedStateHandle): V
13 | }
14 |
15 | class GenericSavedStateViewModelFactory(
16 | private val viewModelFactory: ViewModelFactory,
17 | owner: SavedStateRegistryOwner,
18 | defaultArgs: Bundle? = null
19 | ) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
20 | @Suppress("UNCHECKED_CAST")
21 | override fun create(
22 | key: String,
23 | modelClass: Class,
24 | handle: SavedStateHandle
25 | ): T {
26 | return viewModelFactory.create(handle) as T
27 | }
28 | }
29 |
30 | /**
31 | * Convenience function to use with `by viewModels` that creates an instance of
32 | * [AbstractSavedStateViewModelFactory] that enables us to pass [SavedStateHandle]
33 | * to the [ViewModel]'s constructor.
34 | *
35 | * @param factory instance of [ViewModelFactory] that will be used to construct the [ViewModel]
36 | * @param owner instance of Fragment or Activity that owns the [ViewModel]
37 | * @param defaultArgs Bundle with default values to populate the [SavedStateHandle]
38 | *
39 | * @see ViewModelFactory
40 | */
41 | @MainThread
42 | inline fun SavedStateRegistryOwner.withFactory(
43 | factory: ViewModelFactory,
44 | defaultArgs: Bundle? = null
45 | ) = GenericSavedStateViewModelFactory(factory, this, defaultArgs)
46 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/java/com/smarttoolfactory/dashboard/viewbindings/DashboardViewBindings.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.dashboard.viewbindings
2 |
3 | import android.graphics.Color
4 | import android.widget.ImageButton
5 | import android.widget.ImageView
6 | import androidx.databinding.BindingAdapter
7 | import com.bumptech.glide.Glide
8 | import com.bumptech.glide.request.RequestOptions
9 | import com.smarttoolfactory.dashboard.R
10 |
11 | /**
12 | * Binding adapter used with this class android:src used with binding of this object
13 | * loads image from url into specified view
14 | *
15 | * @param view image to be loaded into
16 | * @param path of the image to be fetched
17 | */
18 | @BindingAdapter("imageSrc")
19 | fun setImageUrl(view: ImageView, path: String?) {
20 |
21 | try {
22 |
23 | val requestOptions = RequestOptions()
24 | requestOptions.placeholder(R.drawable.placeholder)
25 |
26 | Glide
27 | .with(view.context)
28 | .setDefaultRequestOptions(requestOptions)
29 | .load(path)
30 | .into(view)
31 | } catch (e: Exception) {
32 | e.printStackTrace()
33 | }
34 | }
35 |
36 | @BindingAdapter("favoriteImageSrc")
37 | fun ImageButton.setFavoriteImageSrc(favorite: Boolean) {
38 |
39 | if (favorite) {
40 | setColorFilter(Color.rgb(244, 81, 30))
41 | } else {
42 | setColorFilter(Color.rgb(41, 182, 246))
43 | }
44 |
45 | val imageResource = if (favorite) R.drawable.ic_baseline_favorite_30
46 | else R.drawable.ic_baseline_favorite_border_30
47 |
48 | setImageResource(imageResource)
49 | }
50 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/res/drawable/ic_baseline_architecture_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
16 |
17 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/res/drawable/ic_baseline_bathtub_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
14 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/res/drawable/ic_baseline_brightness_low_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/res/drawable/ic_baseline_favorite_30.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/res/drawable/ic_baseline_favorite_border_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/res/drawable/ic_baseline_favorite_border_30.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/res/drawable/ic_baseline_hotel_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/res/drawable/ic_baseline_remove_red_eye_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/res/drawable/ic_baseline_sort_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/res/drawable/placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/PropertyFindAR/5d2b510a8d1c489a5520b1fff6a143552e50ca79/features/dashboard/src/main/res/drawable/placeholder.png
--------------------------------------------------------------------------------
/features/dashboard/src/main/res/layout/fragment_bar_chart.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/res/layout/fragment_dashboard.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
9 |
10 |
13 |
14 |
15 |
19 |
20 |
30 |
31 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/res/layout/fragment_dashboard_see_all.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
10 |
11 |
15 |
16 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/res/layout/item_chart_bar.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/res/layout/item_chart_trends.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/res/layout/item_loading.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/res/layout/item_recommended_section.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
16 |
17 |
30 |
31 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/features/dashboard/src/main/res/navigation/nav_graph_dashboard.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
18 |
19 |
22 |
23 |
24 |
25 |
30 |
31 |
34 |
35 |
38 |
39 |
40 |
41 |
42 |
47 |
48 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/features/dashboard/src/test/java/com/smarttoolfactory/dashboard/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.dashboard
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/features/home/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/features/home/src/androidTest/java/com/smarttoolfactory/home/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.home
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumented test, which will execute on an Android device.
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | fun useAppContext() {
18 | // Context of the app under test.
19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20 | assertEquals("com.smarttoolfactory.home", appContext.packageName)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/features/home/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/features/home/src/main/java/com/smarttoolfactory/home/adapter/LoadingAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.home.adapter
2 |
3 | class LoadingAdapter
4 |
--------------------------------------------------------------------------------
/features/home/src/main/java/com/smarttoolfactory/home/adapter/model/PropertyListModel.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.home.adapter.model
2 |
3 | import android.os.Parcelable
4 | import com.smarttoolfactory.domain.model.PropertyItem
5 | import kotlinx.android.parcel.Parcelize
6 |
7 | /**
8 | * Model for list that has a title,
9 | * and data for horizontally scrollable items in RecyclerView
10 | */
11 | @Parcelize
12 | data class PropertyListModel(val transitionName: String, val items: List) : Parcelable
13 |
--------------------------------------------------------------------------------
/features/home/src/main/java/com/smarttoolfactory/home/di/HomeComponent.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.home.di
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.smarttoolfactory.core.di.CoreModuleDependencies
5 | import com.smarttoolfactory.home.propertylist.flow.PropertyListFragment
6 | import com.smarttoolfactory.home.propertylist.paged.PagedPropertyListFragment
7 | import com.smarttoolfactory.home.propertylist.rxjava.PropertyListFragmentRxJava3
8 | import dagger.BindsInstance
9 | import dagger.Component
10 |
11 | @Component(
12 | dependencies = [CoreModuleDependencies::class],
13 | modules = [HomeModule::class]
14 | )
15 | interface HomeComponent {
16 |
17 | // Fragments of ViewPager2 in Home Fragment
18 | fun inject(fragment: PropertyListFragment)
19 | fun inject(fragment: PropertyListFragmentRxJava3)
20 | fun inject(fragment: PagedPropertyListFragment)
21 |
22 | @Component.Factory
23 | interface Factory {
24 | fun create(
25 | dependentModule: CoreModuleDependencies,
26 | @BindsInstance fragment: Fragment
27 | ): HomeComponent
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/features/home/src/main/java/com/smarttoolfactory/home/propertylist/AbstractPropertyListVM.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.home.propertylist
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 |
19 | abstract val goToDetailScreen: LiveData>
20 |
21 | abstract val propertyListViewState: LiveData>>
22 |
23 | /**
24 | * Used when fragment is just opened
25 | */
26 | abstract fun getPropertyList()
27 |
28 | abstract fun refreshPropertyList(orderBy: String? = null)
29 |
30 | abstract fun onClick(propertyItem: PropertyItem)
31 | }
32 |
--------------------------------------------------------------------------------
/features/home/src/main/java/com/smarttoolfactory/home/viewbindings/HomeViewBindings.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.home.viewbindings
2 |
3 | import android.widget.ImageButton
4 | import android.widget.ImageView
5 | import androidx.databinding.BindingAdapter
6 | import com.bumptech.glide.Glide
7 | import com.bumptech.glide.request.RequestOptions
8 |
9 | /**
10 | * Binding adapter used with this class android:src used with binding of this object
11 | * loads image from url into specified view
12 | *
13 | * @param view image to be loaded into
14 | * @param path of the image to be fetched
15 | */
16 | @BindingAdapter("imageSrc")
17 | fun setImageUrl(view: ImageView, path: String?) {
18 |
19 | try {
20 |
21 | val requestOptions = RequestOptions()
22 |
23 | Glide
24 | .with(view.context)
25 | .setDefaultRequestOptions(requestOptions)
26 | .load(path)
27 | .into(view)
28 | } catch (e: Exception) {
29 | e.printStackTrace()
30 | }
31 | }
32 |
33 | @BindingAdapter("favoriteImageSrc")
34 | fun ImageButton.setFavoriteImageSrc(favorite: Boolean) {
35 |
36 | val stateSet =
37 | intArrayOf(android.R.attr.state_checked * if (favorite) 1 else -1)
38 | setImageState(stateSet, true)
39 |
40 | // val animatedVectorDrawable = if (favorite) {
41 | // AppCompatResources.getDrawable(
42 | // context,
43 | // R.drawable.avd_heart_favorite
44 | // ) as? AnimatedVectorDrawable
45 | // } else {
46 | // AppCompatResources.getDrawable(
47 | // context,
48 | // R.drawable.avd_heart_empty
49 | // ) as? AnimatedVectorDrawable
50 | // }
51 | // setImageDrawable(animatedVectorDrawable)
52 | }
53 |
--------------------------------------------------------------------------------
/features/home/src/main/java/com/smarttoolfactory/home/viewmodel/HomeToolbarVM.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.home.viewmodel
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import androidx.lifecycle.ViewModel
5 | import com.smarttoolfactory.core.util.Event
6 | import com.smarttoolfactory.domain.ORDER_BY_BEDS_ASCENDING
7 | import com.smarttoolfactory.domain.ORDER_BY_DES_DESCENDING
8 | import com.smarttoolfactory.domain.ORDER_BY_NONE
9 | import com.smarttoolfactory.domain.ORDER_BY_PRICE_ASCENDING
10 | import com.smarttoolfactory.domain.ORDER_BY_PRICE_DESCENDING
11 | import dagger.hilt.android.lifecycle.HiltViewModel
12 | import javax.inject.Inject
13 |
14 | @HiltViewModel
15 | class HomeToolbarVM @Inject constructor() : ViewModel() {
16 |
17 | var currentSortFilter = ORDER_BY_NONE
18 |
19 | val sortPropertyList = listOf(
20 | ORDER_BY_NONE,
21 | ORDER_BY_PRICE_ASCENDING,
22 | ORDER_BY_PRICE_DESCENDING,
23 | ORDER_BY_BEDS_ASCENDING,
24 | ORDER_BY_DES_DESCENDING
25 | )
26 |
27 | val sortFilterNames = listOf(
28 | "Featured",
29 | "Price Ascending",
30 | "Price Descending",
31 | "Beds Ascending",
32 | "Beds Descending"
33 | )
34 |
35 | val queryBySort = MutableLiveData>()
36 | }
37 |
--------------------------------------------------------------------------------
/features/home/src/main/res/anim/fade_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
23 |
--------------------------------------------------------------------------------
/features/home/src/main/res/anim/fade_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
23 |
--------------------------------------------------------------------------------
/features/home/src/main/res/anim/slide_in_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
--------------------------------------------------------------------------------
/features/home/src/main/res/anim/slide_in_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/features/home/src/main/res/anim/slide_out_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
--------------------------------------------------------------------------------
/features/home/src/main/res/anim/slide_out_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
--------------------------------------------------------------------------------
/features/home/src/main/res/drawable/asl_heart_break.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
12 |
13 |
17 |
18 |
22 |
23 |
--------------------------------------------------------------------------------
/features/home/src/main/res/drawable/ic_baseline_architecture_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
16 |
17 |
--------------------------------------------------------------------------------
/features/home/src/main/res/drawable/ic_baseline_favorite_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/features/home/src/main/res/drawable/ic_baseline_favorite_30.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/features/home/src/main/res/drawable/ic_baseline_favorite_border_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/features/home/src/main/res/drawable/ic_baseline_favorite_border_30.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/features/home/src/main/res/drawable/ic_baseline_hotel_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/features/home/src/main/res/drawable/ic_baseline_remove_red_eye_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/features/home/src/main/res/drawable/ic_baseline_sort_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/features/home/src/main/res/drawable/ic_outline_airline_seat_individual_suite_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/features/home/src/main/res/drawable/placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/PropertyFindAR/5d2b510a8d1c489a5520b1fff6a143552e50ca79/features/home/src/main/res/drawable/placeholder.png
--------------------------------------------------------------------------------
/features/home/src/main/res/drawable/vd_heart_empty.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/features/home/src/main/res/drawable/vd_heart_filled.xml:
--------------------------------------------------------------------------------
1 |
7 |
13 |
--------------------------------------------------------------------------------
/features/home/src/main/res/layout/fragment_navhost_property_list_flow.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/features/home/src/main/res/layout/fragment_navhost_property_list_paged.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/features/home/src/main/res/layout/fragment_navhost_property_list_rxjava3.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/features/home/src/main/res/menu/menu_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/features/home/src/main/res/navigation/nav_graph_property_list_flow.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
17 |
18 |
19 |
24 |
25 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/features/home/src/main/res/navigation/nav_graph_property_list_paged.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
17 |
18 |
19 |
24 |
25 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/features/home/src/main/res/navigation/nav_graph_property_list_rxjava3.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
17 |
18 |
19 |
24 |
25 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/features/home/src/test/java/com/smarttoolfactory/home/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.home
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/features/notification/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/features/notification/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import extension.addBaseDynamicFeatureModuleDependencies
2 | import extension.addInstrumentationTestDependencies
3 | import extension.addUnitTestDependencies
4 |
5 | plugins {
6 | id(Plugins.ANDROID_DYNAMIC_FEATURE_PLUGIN)
7 | id(Plugins.KOTLIN_ANDROID_PLUGIN)
8 | id(Plugins.KOTLIN_ANDROID_EXTENSIONS_PLUGIN)
9 | id(Plugins.KOTLIN_KAPT_PLUGIN)
10 | id(Plugins.DAGGER_HILT_PLUGIN)
11 | id(Plugins.NAVIGATION_SAFE_ARGS)
12 | }
13 |
14 | android {
15 |
16 | compileSdkVersion(AndroidVersion.COMPILE_SDK_VERSION)
17 |
18 | defaultConfig {
19 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
20 | }
21 |
22 | buildTypes {
23 | getByName("release") {
24 | isMinifyEnabled = false
25 | proguardFiles(
26 | getDefaultProguardFile("proguard-android-optimize.txt"),
27 | "proguard-rules.pro"
28 | )
29 | }
30 | }
31 |
32 | packagingOptions {
33 | exclude("META-INF/AL2.0")
34 | }
35 |
36 | dataBinding.isEnabled = true
37 | // android.buildFeatures.viewBinding = true
38 |
39 | compileOptions {
40 | sourceCompatibility = JavaVersion.VERSION_1_8
41 | targetCompatibility = JavaVersion.VERSION_1_8
42 | }
43 | kotlinOptions {
44 | jvmTarget = "1.8"
45 | }
46 | }
47 |
48 | dependencies {
49 |
50 | implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
51 |
52 | implementation(project(Modules.APP))
53 | implementation(project(Modules.AndroidLibrary.CORE))
54 | implementation(project(Modules.AndroidLibrary.DOMAIN))
55 |
56 | addBaseDynamicFeatureModuleDependencies()
57 |
58 | // Support and Widgets
59 | implementation(Deps.APPCOMPAT)
60 | implementation(Deps.MATERIAL)
61 | implementation(Deps.CONSTRAINT_LAYOUT)
62 |
63 | // Glide
64 | implementation(Deps.GLIDE)
65 | kapt(Deps.GLIDE_COMPILER)
66 |
67 | // Lottie
68 | implementation(Deps.LOTTIE)
69 |
70 | // Unit Tests
71 | addUnitTestDependencies()
72 | testImplementation(project(Modules.AndroidLibrary.TEST_UTILS))
73 |
74 | // Instrumentation Tests
75 | addInstrumentationTestDependencies()
76 | androidTestImplementation(project(Modules.AndroidLibrary.TEST_UTILS))
77 | }
78 |
--------------------------------------------------------------------------------
/features/notification/src/androidTest/java/com/smarttoolfactory/notification/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.notification
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumented test, which will execute on an Android device.
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | fun useAppContext() {
18 | // Context of the app under test.
19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20 | assertEquals("com.smarttoolfactory.notification", appContext.packageName)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/features/notification/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/features/notification/src/main/java/com/smarttoolfactory/notification/NotificationFragment.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.notification
2 |
3 | import com.smarttoolfactory.core.ui.fragment.DynamicNavigationFragment
4 | import com.smarttoolfactory.notification.databinding.FragmentNotificationBinding
5 |
6 | class NotificationFragment : DynamicNavigationFragment() {
7 |
8 | override fun getLayoutRes(): Int = R.layout.fragment_notification
9 | }
10 |
--------------------------------------------------------------------------------
/features/notification/src/main/res/layout/fragment_notification.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
22 |
23 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/features/notification/src/main/res/navigation/nav_graph_notification.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/features/notification/src/test/java/com/smarttoolfactory/notification/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.notification
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/features/property_detail/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/features/property_detail/src/androidTest/java/com/smarttoolfactory/property_detail/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.property_detail
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumented test, which will execute on an Android device.
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | fun useAppContext() {
18 | // Context of the app under test.
19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20 | assertEquals("com.smarttoolfactory.property_detail", appContext.packageName)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/features/property_detail/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/features/property_detail/src/main/java/com/smarttoolfactory/property_detail/DetailViewBindings.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.property_detail
2 |
3 | import android.widget.ImageView
4 | import androidx.databinding.BindingAdapter
5 | import com.bumptech.glide.Glide
6 | import com.bumptech.glide.request.RequestOptions
7 |
8 | /**
9 | * Binding adapter used with this class android:src used with binding of this object
10 | * loads image from url into specified view
11 | *
12 | * @param view image to be loaded into
13 | * @param path of the image to be fetched
14 | */
15 | @BindingAdapter("imageSrc")
16 | fun setImageUrl(view: ImageView, path: String?) {
17 |
18 | try {
19 |
20 | val requestOptions = RequestOptions()
21 | requestOptions.placeholder(R.drawable.placeholder)
22 |
23 | Glide
24 | .with(view.context)
25 | .setDefaultRequestOptions(requestOptions)
26 | .load(path)
27 | .into(view)
28 | } catch (e: Exception) {
29 | e.printStackTrace()
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/features/property_detail/src/main/java/com/smarttoolfactory/property_detail/PropertyDetailViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.property_detail
2 |
3 | import androidx.lifecycle.ViewModel
4 | import androidx.lifecycle.ViewModelProvider
5 | import com.smarttoolfactory.domain.usecase.property.SetPropertyStatsUseCase
6 | import javax.inject.Inject
7 | import kotlinx.coroutines.CoroutineScope
8 |
9 | class PropertyDetailViewModel @Inject constructor(
10 | private val coroutineScope: CoroutineScope,
11 | private val setPropertyStatusUseCase: SetPropertyStatsUseCase
12 | ) : ViewModel()
13 |
14 | class PropertyDetailViewModelFactory @Inject constructor(
15 | private val coroutineScope: CoroutineScope,
16 | private val setPropertyStatusUseCase: SetPropertyStatsUseCase
17 | ) : ViewModelProvider.Factory {
18 |
19 | @Suppress("UNCHECKED_CAST")
20 | override fun create(modelClass: Class): T {
21 | if (modelClass != PropertyDetailViewModel::class.java) {
22 | throw IllegalArgumentException("Unknown ViewModel class")
23 | }
24 | return PropertyDetailViewModel(
25 | coroutineScope,
26 | setPropertyStatusUseCase
27 | ) as T
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/features/property_detail/src/main/java/com/smarttoolfactory/property_detail/di/PropertyDetailComponent.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.property_detail.di
2 |
3 | import androidx.fragment.app.Fragment
4 | import com.smarttoolfactory.core.di.CoreModuleDependencies
5 | import com.smarttoolfactory.property_detail.PropertyDetailFragment
6 | import dagger.BindsInstance
7 | import dagger.Component
8 |
9 | @Component(
10 | dependencies = [CoreModuleDependencies::class],
11 | modules = [PropertyDetailModule::class]
12 | )
13 | interface PropertyDetailComponent {
14 |
15 | fun inject(fragment: PropertyDetailFragment)
16 |
17 | @Component.Factory
18 | interface Factory {
19 | fun create(
20 | dependentModule: CoreModuleDependencies,
21 | @BindsInstance fragment: Fragment
22 | ): PropertyDetailComponent
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/features/property_detail/src/main/java/com/smarttoolfactory/property_detail/di/PropertyDetailModule.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.property_detail.di
2 |
3 | import androidx.fragment.app.Fragment
4 | import androidx.lifecycle.ViewModelProvider
5 | import com.smarttoolfactory.property_detail.PropertyDetailViewModel
6 | import com.smarttoolfactory.property_detail.PropertyDetailViewModelFactory
7 | import dagger.Module
8 | import dagger.Provides
9 | import dagger.hilt.InstallIn
10 | import dagger.hilt.android.components.FragmentComponent
11 | import kotlinx.coroutines.CoroutineScope
12 | import kotlinx.coroutines.Dispatchers
13 | import kotlinx.coroutines.SupervisorJob
14 |
15 | @InstallIn(FragmentComponent::class)
16 | @Module
17 | class PropertyDetailModule {
18 |
19 | @Provides
20 | fun providePostDetailViewModel(fragment: Fragment, factory: PropertyDetailViewModelFactory) =
21 | ViewModelProvider(fragment, factory).get(PropertyDetailViewModel::class.java)
22 |
23 | @Provides
24 | fun provideCoroutineScope() = CoroutineScope(Dispatchers.Main.immediate + SupervisorJob())
25 | }
26 |
--------------------------------------------------------------------------------
/features/property_detail/src/main/res/drawable/placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/PropertyFindAR/5d2b510a8d1c489a5520b1fff6a143552e50ca79/features/property_detail/src/main/res/drawable/placeholder.png
--------------------------------------------------------------------------------
/features/property_detail/src/main/res/navigation/nav_graph_property_detail.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/features/property_detail/src/test/java/com/smarttoolfactory/property_detail/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.property_detail
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/PropertyFindAR/5d2b510a8d1c489a5520b1fff6a143552e50ca79/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Aug 02 11:05:47 TRT 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/libraries/core/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/libraries/core/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import extension.addCoreModuleDependencies
2 | import extension.addInstrumentationTestDependencies
3 | import extension.addUnitTestDependencies
4 |
5 | plugins {
6 | id(Plugins.ANDROID_LIBRARY_PLUGIN)
7 | id(Plugins.KOTLIN_ANDROID_PLUGIN)
8 | id(Plugins.KOTLIN_ANDROID_EXTENSIONS_PLUGIN)
9 | id(Plugins.KOTLIN_KAPT_PLUGIN)
10 | id(Plugins.DAGGER_HILT_PLUGIN)
11 | }
12 |
13 | android {
14 |
15 | compileSdk = AndroidVersion.COMPILE_SDK_VERSION
16 | defaultConfig {
17 | minSdk = AndroidVersion.MIN_SDK_VERSION
18 | targetSdk = AndroidVersion.TARGET_SDK_VERSION
19 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
20 | }
21 |
22 | buildTypes {
23 | getByName("release") {
24 | isMinifyEnabled = false
25 | proguardFiles(
26 | getDefaultProguardFile("proguard-android-optimize.txt"),
27 | "proguard-rules.pro"
28 | )
29 | }
30 | }
31 |
32 | android.buildFeatures.dataBinding = true
33 |
34 | compileOptions {
35 | sourceCompatibility = JavaVersion.VERSION_1_8
36 | targetCompatibility = JavaVersion.VERSION_1_8
37 | }
38 |
39 | kotlinOptions {
40 | jvmTarget = "1.8"
41 | }
42 |
43 | testOptions {
44 | unitTests.isIncludeAndroidResources = true
45 | }
46 | }
47 |
48 | dependencies {
49 |
50 | implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
51 |
52 | implementation(project(Modules.AndroidLibrary.DOMAIN))
53 | implementation(project(Modules.AndroidLibrary.DATA))
54 |
55 | addCoreModuleDependencies()
56 |
57 | addUnitTestDependencies()
58 | testImplementation(project(Modules.AndroidLibrary.TEST_UTILS))
59 |
60 | addInstrumentationTestDependencies()
61 | androidTestImplementation(project(Modules.AndroidLibrary.TEST_UTILS))
62 | }
63 |
--------------------------------------------------------------------------------
/libraries/core/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/PropertyFindAR/5d2b510a8d1c489a5520b1fff6a143552e50ca79/libraries/core/consumer-rules.pro
--------------------------------------------------------------------------------
/libraries/core/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/libraries/core/src/androidTest/java/com/smarttoolfactory/core/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.core
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumented test, which will execute on an Android device.
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | fun useAppContext() {
18 | // Context of the app under test.
19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20 | assertEquals("com.smarttoolfactory.core.test", appContext.packageName)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/libraries/core/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/smarttoolfactory/core/di/CoreModule.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.core.di
2 |
3 | import com.smarttoolfactory.domain.dispatcher.UseCaseDispatchers
4 | import dagger.Module
5 | import dagger.Provides
6 | import dagger.hilt.InstallIn
7 | import dagger.hilt.components.SingletonComponent
8 | import javax.inject.Singleton
9 | import kotlinx.coroutines.CoroutineScope
10 | import kotlinx.coroutines.Dispatchers
11 | import kotlinx.coroutines.SupervisorJob
12 |
13 | @InstallIn(SingletonComponent::class)
14 | @Module(includes = [DataModule::class])
15 | class CoreModule {
16 |
17 | @Singleton
18 | @Provides
19 | fun provideCoroutineScope() = CoroutineScope(Dispatchers.Main.immediate + SupervisorJob())
20 |
21 | @Provides
22 | fun provideUseCaseDispatchers(): UseCaseDispatchers {
23 | return UseCaseDispatchers(Dispatchers.IO, Dispatchers.Default, Dispatchers.Main)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/smarttoolfactory/core/di/CoreModuleDependencies.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.core.di
2 |
3 | import com.smarttoolfactory.domain.usecase.property.GetDashboardStatsUseCase
4 | import com.smarttoolfactory.domain.usecase.property.GetPropertiesUseCaseFlow
5 | import com.smarttoolfactory.domain.usecase.property.GetPropertiesUseCasePaged
6 | import com.smarttoolfactory.domain.usecase.property.GetPropertiesUseCaseRxJava3
7 | import com.smarttoolfactory.domain.usecase.property.SetPropertyStatsUseCase
8 | import com.smarttoolfactory.domain.usecase.property.SetPropertyStatsUseCaseRxJava3
9 | import dagger.hilt.EntryPoint
10 | import dagger.hilt.InstallIn
11 | import dagger.hilt.components.SingletonComponent
12 |
13 | /**
14 | * This component is required for adding dependencies to Dynamic Feature Modules by
15 | * adding [CoreModule] as dependent component
16 | */
17 | @EntryPoint
18 | @InstallIn(SingletonComponent::class)
19 | interface CoreModuleDependencies {
20 |
21 | /*
22 | Provision methods to provide dependencies to components that depend on this component
23 | */
24 | fun getPropertiesUseCaseFlow(): GetPropertiesUseCaseFlow
25 | fun getPropertiesUseCaseRxJava3(): GetPropertiesUseCaseRxJava3
26 | fun getPropertiesUseCasePaged(): GetPropertiesUseCasePaged
27 |
28 | // Set property like or view status
29 | fun setPropertyStatsUseCase(): SetPropertyStatsUseCase
30 | fun setPropertyStatsUseCaseRxJava3(): SetPropertyStatsUseCaseRxJava3
31 |
32 | // Dashboard stats for properties and info
33 | fun getDashboardStatsUseCase(): GetDashboardStatsUseCase
34 | }
35 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/smarttoolfactory/core/di/qualifier/RecycledViewPool.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.core.di.qualifier
2 |
3 | import javax.inject.Qualifier
4 |
5 | @Qualifier
6 | @Retention(AnnotationRetention.RUNTIME)
7 | annotation class RecycledViewPool(val value: Type) {
8 |
9 | enum class Type {
10 | PROPERTY_ITEM,
11 | PROPERTY_HORIZONTAL,
12 | CHART_ITEM
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/smarttoolfactory/core/di/scope/FeatureScope.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.core.di.scope
2 |
3 | import javax.inject.Scope
4 |
5 | /**
6 | * Scope to be used in dynamic feature modules
7 | */
8 | @Scope
9 | @kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
10 | annotation class FeatureScope
11 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/smarttoolfactory/core/error/NavigationException.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.core.error
2 |
3 | class NavigationException(override val message: String?) : Exception(message)
4 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/smarttoolfactory/core/ui/recyclerview/adapter/ItemDiffCallback.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.core.ui.recyclerview.adapter
2 |
3 | import androidx.recyclerview.widget.DiffUtil
4 |
5 | class ItemDiffCallback(
6 | private val viewBinders: Map
7 | ) : DiffUtil.ItemCallback() {
8 |
9 | override fun areItemsTheSame(oldItem: Any, newItem: Any): Boolean {
10 | if (oldItem::class != newItem::class) {
11 | return false
12 | }
13 | return viewBinders[oldItem::class.java]?.areItemsTheSame(oldItem, newItem) ?: false
14 | }
15 |
16 | override fun areContentsTheSame(oldItem: Any, newItem: Any): Boolean {
17 | // We know the items are the same class because [areItemsTheSame] returned true
18 | return viewBinders[oldItem::class.java]?.areContentsTheSame(oldItem, newItem) ?: false
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/smarttoolfactory/core/ui/recyclerview/adapter/ViewBinders.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.core.ui.recyclerview.adapter
2 |
3 | import android.view.ViewGroup
4 | import androidx.recyclerview.widget.DiffUtil
5 | import androidx.recyclerview.widget.RecyclerView
6 |
7 | typealias ItemClazz = Class
8 | typealias MappableItemBinder = MappableItemViewBinder
9 | typealias ItemBinder = BaseItemViewBinder
10 |
11 | /**
12 | * [MappableItemViewBinder] has 3 way relationship which can map model [Class]
13 | * either to ViewHolder type and layout type to ViewHolder type which makes
14 | * this ViewBinder unique for a layout and model type.
15 | *
16 | * * Whenever data is submitted with different types
17 | */
18 | abstract class MappableItemViewBinder(
19 | val modelClazz: Class
20 | ) : BaseItemViewBinder()
21 |
22 | /**
23 | * ViewBinder that maps Layout to [RecyclerView.ViewHolder] which let's this ViewHolder has
24 | * model and ViewHolder couple which can only be used adapters with single layout type
25 | */
26 | abstract class BaseItemViewBinder : DiffUtil.ItemCallback() {
27 |
28 | abstract fun createViewHolder(parent: ViewGroup): RecyclerView.ViewHolder
29 | abstract fun bindViewHolder(model: M, viewHolder: VH)
30 | abstract fun getItemLayoutResource(): Int
31 |
32 | // Having these as non abstract because not all the viewBinders are required to implement them.
33 | open fun onViewRecycled(viewHolder: VH) = Unit
34 | open fun onViewDetachedFromWindow(viewHolder: VH) = Unit
35 | }
36 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/smarttoolfactory/core/ui/recyclerview/itemcallback/DefaultItemCallback.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.core.ui.recyclerview.itemcallback
2 |
3 | import androidx.recyclerview.widget.DiffUtil
4 |
5 | /**
6 | * Base [DiffUtil.ItemCallback] that calculates the difference between two lists
7 | * and outputs a list of update operations that converts the first list into the second one.
8 | *
9 | * * With data classes have same items in constructor will have same **hash code** so using
10 | * hash code for [DiffUtil.ItemCallback.areContentsTheSame]
11 | */
12 | class DefaultItemCallback : DiffUtil.ItemCallback() {
13 |
14 | override fun areItemsTheSame(
15 | oldItem: ItemType,
16 | newItem: ItemType
17 | ): Boolean {
18 | return oldItem == newItem
19 | }
20 |
21 | override fun areContentsTheSame(
22 | oldItem: ItemType,
23 | newItem: ItemType
24 | ): Boolean {
25 | return oldItem.hashCode() == newItem.hashCode()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/smarttoolfactory/core/ui/recyclerview/itemcallback/PropertyItemCallback.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.core.ui.recyclerview.itemcallback
2 |
3 | import androidx.recyclerview.widget.DiffUtil
4 | import com.smarttoolfactory.domain.model.PropertyItem
5 |
6 | class PropertyItemCallback : DiffUtil.ItemCallback() {
7 |
8 | override fun areItemsTheSame(
9 | oldItem: PropertyItem,
10 | newItem: PropertyItem
11 | ): Boolean {
12 | return oldItem.id == newItem.id
13 | }
14 |
15 | override fun areContentsTheSame(
16 | oldItem: PropertyItem,
17 | newItem: PropertyItem
18 | ): Boolean {
19 | return oldItem.hashCode() == newItem.hashCode()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/smarttoolfactory/core/util/EndlessScrollListener.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.core.util
2 |
3 | import androidx.recyclerview.widget.LinearLayoutManager
4 | import androidx.recyclerview.widget.RecyclerView
5 |
6 | class EndlessScrollListener(
7 | private val linearLayoutManager: LinearLayoutManager,
8 | private val listener: ScrollToBottomListener
9 | ) : RecyclerView.OnScrollListener() {
10 |
11 | private var previousTotal = 0
12 | private var loading = true
13 | private val visibleThreshold = 8
14 | private var firstVisibleItem = 0
15 | private var visibleItemCount = 0
16 | private var totalItemCount = 0
17 |
18 | fun onRefresh() {
19 | previousTotal = 0
20 | }
21 |
22 | override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
23 | super.onScrolled(recyclerView, dx, dy)
24 |
25 | visibleItemCount = recyclerView.childCount
26 | totalItemCount = linearLayoutManager.itemCount
27 | firstVisibleItem = linearLayoutManager.findFirstVisibleItemPosition()
28 |
29 | if (loading) {
30 | if (totalItemCount > previousTotal) {
31 | loading = false
32 | previousTotal = totalItemCount
33 | }
34 | }
35 | if (!loading && totalItemCount - visibleItemCount
36 | <= firstVisibleItem + visibleThreshold
37 | ) {
38 | listener.onScrollToBottom()
39 | loading = true
40 | }
41 | }
42 |
43 | interface ScrollToBottomListener {
44 | fun onScrollToBottom()
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/smarttoolfactory/core/util/Event.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2019 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.smarttoolfactory.core.util
17 |
18 | import androidx.lifecycle.Observer
19 |
20 | /**
21 | * Used as a wrapper for data that is exposed via a LiveData that represents an event.
22 | */
23 | open class Event(private val content: T) {
24 |
25 | @Suppress("MemberVisibilityCanBePrivate")
26 | var hasBeenHandled = false
27 | private set // Allow external read but not write
28 |
29 | /**
30 | * Returns the content and prevents its use again.
31 | */
32 | fun getContentIfNotHandled(): T? {
33 | return if (hasBeenHandled) {
34 | null
35 | } else {
36 | hasBeenHandled = true
37 | content
38 | }
39 | }
40 |
41 | /**
42 | * Returns the content, even if it's already been handled.
43 | */
44 | fun peekContent(): T = content
45 | }
46 |
47 | /**
48 | * An [Observer] for [Event]s, simplifying the pattern of checking if the [Event]'s content has
49 | * already been handled.
50 | *
51 | * [onEventUnhandledContent] is *only* called if the [Event]'s contents has not been handled.
52 | */
53 | class EventObserver(private val onEventUnhandledContent: (T) -> Unit) : Observer> {
54 | override fun onChanged(event: Event?) {
55 | event?.getContentIfNotHandled()?.let {
56 | onEventUnhandledContent(it)
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/smarttoolfactory/core/util/FlowViewStateExtension.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.core.util
2 |
3 | import com.smarttoolfactory.core.viewstate.Status
4 | import com.smarttoolfactory.core.viewstate.ViewState
5 | import com.smarttoolfactory.domain.error.EmptyDataException
6 | import kotlinx.coroutines.CoroutineDispatcher
7 | import kotlinx.coroutines.Dispatchers
8 | import kotlinx.coroutines.flow.Flow
9 | import kotlinx.coroutines.flow.catch
10 | import kotlinx.coroutines.flow.emitAll
11 | import kotlinx.coroutines.flow.flowOf
12 | import kotlinx.coroutines.flow.flowOn
13 | import kotlinx.coroutines.flow.map
14 |
15 | fun Flow.convertToFlowViewState(
16 | dispatcher: CoroutineDispatcher = Dispatchers.Default
17 | ): Flow> {
18 | return this
19 | .map { list -> ViewState(status = Status.SUCCESS, data = list) }
20 | .catch { cause: Throwable -> emitAll(flowOf(ViewState(Status.ERROR, error = cause))) }
21 | .flowOn(dispatcher)
22 | }
23 |
24 | fun Flow>.convertToFlowListViewState(
25 | dispatcher: CoroutineDispatcher = Dispatchers.Default
26 | ): Flow>> {
27 | return this
28 | .map { list ->
29 | if (list.isNullOrEmpty()) {
30 | throw EmptyDataException("Data is empty")
31 | } else {
32 | ViewState(status = Status.SUCCESS, data = list)
33 | }
34 | }
35 | .catch { cause: Throwable -> emitAll(flowOf(ViewState(Status.ERROR, error = cause))) }
36 | .flowOn(dispatcher)
37 | }
38 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/smarttoolfactory/core/util/LifecycleOwnerExtension.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.core.util
2 |
3 | import androidx.lifecycle.LifecycleOwner
4 | import androidx.lifecycle.LiveData
5 | import androidx.lifecycle.Observer
6 |
7 | fun LifecycleOwner.observe(liveData: LiveData, predicate: (T) -> Unit) {
8 | liveData.observe(this, Observer { it?.let { predicate(it) } })
9 | }
10 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/smarttoolfactory/core/util/NavHostExtension.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.core.util
2 |
3 | import androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment
4 | import androidx.navigation.fragment.NavHostFragment
5 | import com.smarttoolfactory.core.ui.fragment.navhost.FieldProperty
6 | import com.smarttoolfactory.core.viewmodel.NavControllerViewModel
7 |
8 | var DynamicNavHostFragment.viewModel: NavControllerViewModel by FieldProperty {
9 | NavControllerViewModel()
10 | }
11 |
12 | var NavHostFragment.viewModel: NavControllerViewModel by FieldProperty {
13 | NavControllerViewModel()
14 | }
15 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/smarttoolfactory/core/util/ViewBindings.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.core.util
2 |
3 | import android.view.View
4 | import androidx.core.widget.ContentLoadingProgressBar
5 | import androidx.databinding.BindingAdapter
6 | import androidx.recyclerview.widget.ListAdapter
7 | import androidx.recyclerview.widget.RecyclerView
8 |
9 | /**
10 | * [BindingAdapter]s for the binding items to ListAdapter.
11 | */
12 | @BindingAdapter("app:items")
13 | fun RecyclerView.setItems(items: List?) {
14 |
15 | items?.let {
16 | println("ViewBinding setItems()")
17 | (adapter as ListAdapter<*, *>)?.submitList(items)
18 | }
19 | }
20 |
21 | /**
22 | * Display or hide a view based on a condition
23 | *
24 | * @param condition if it's true this View's visibility is set to [View.VISIBLE]
25 | */
26 | @BindingAdapter("visibilityBasedOn")
27 | fun View.visibilityBasedOn(condition: Boolean) {
28 | visibility = if (condition) View.VISIBLE else View.GONE
29 | }
30 |
31 | @BindingAdapter("showWhen")
32 | fun ContentLoadingProgressBar.showWhen(condition: Boolean) {
33 | if (condition) show() else hide()
34 | }
35 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/smarttoolfactory/core/util/ViewExtension.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.core.util
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import android.widget.ImageView
6 | import androidx.annotation.LayoutRes
7 | import androidx.appcompat.app.AppCompatActivity
8 | import androidx.databinding.DataBindingUtil
9 | import androidx.databinding.ViewDataBinding
10 | import com.bumptech.glide.Glide
11 |
12 | inline fun T.executeAfter(block: T.() -> Unit) {
13 | block()
14 | executePendingBindings()
15 | }
16 |
17 | inline fun ViewGroup.inflate(
18 | @LayoutRes layout: Int,
19 | attachToRoot: Boolean = false
20 | ): T = DataBindingUtil.inflate(
21 | LayoutInflater.from(context),
22 | layout,
23 | this,
24 | attachToRoot
25 | )
26 |
27 | fun ImageView.clearResources() {
28 | if ((context as? AppCompatActivity)?.isDestroyed == true) return
29 | Glide.with(context).clear(this)
30 | setImageDrawable(null)
31 | }
32 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/smarttoolfactory/core/viewmodel/NavControllerViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.core.viewmodel
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import androidx.lifecycle.ViewModel
5 | import androidx.navigation.NavController
6 | import com.smarttoolfactory.core.util.Event
7 |
8 | /**
9 | * ViewModel shared by Activity and fragmgents that contains the [NavController] belongs to
10 | * current fragment on screen.
11 | *
12 | * * Usable to change Toolbar status based on fragment currently navigated
13 | */
14 | class NavControllerViewModel : ViewModel() {
15 | val currentNavController = MutableLiveData>()
16 | }
17 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/smarttoolfactory/core/viewmodel/PropertyDetailNavigationVM.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.core.viewmodel
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import androidx.lifecycle.ViewModel
5 | import com.smarttoolfactory.core.util.Event
6 | import com.smarttoolfactory.domain.model.PropertyItem
7 |
8 | /**
9 | * ViewModel for navigating to detail from different layers of the app. When the [PropertyItem]
10 | *
11 | * of this model is set from a list fragment specified can navigate to detail screen
12 | *
13 | * * Navigate from Main fragment in **App module**
14 | * that replaces the one that contains BottomNavigationView
15 | *
16 | * * Navigate from Home fragment in **Home module** that contains ViewPager2
17 | * * Navigate from individual property list fragments
18 | */
19 | class PropertyDetailNavigationVM : ViewModel() {
20 |
21 | /**
22 | * LiveData to navigate Property detail from MainFragment in app module
23 | */
24 | val goToPropertyDetailFromMain = MutableLiveData>()
25 |
26 | /**
27 | * LiveData to navigate Property detail from HomeFragment in Home dynamic feature module
28 | */
29 | val goToPropertyDetailFromHome = MutableLiveData>()
30 | }
31 |
--------------------------------------------------------------------------------
/libraries/core/src/main/java/com/smarttoolfactory/core/viewstate/ViewState.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.core.viewstate
2 |
3 | class ViewState(
4 | val status: Status,
5 | val data: T? = null,
6 | val error: Throwable? = null
7 | ) {
8 |
9 | fun isSuccess() = status == Status.SUCCESS
10 |
11 | fun isLoading() = status == Status.LOADING
12 |
13 | fun getErrorMessage() = error?.message
14 |
15 | fun shouldShowErrorMessage() = error != null && status == Status.ERROR
16 | }
17 |
18 | enum class Status {
19 | LOADING,
20 | SUCCESS,
21 | ERROR
22 | }
23 |
--------------------------------------------------------------------------------
/libraries/core/src/test/java/com/smarttoolfactory/core/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.core
2 |
3 | import junit.framework.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/libraries/data/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/libraries/data/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/PropertyFindAR/5d2b510a8d1c489a5520b1fff6a143552e50ca79/libraries/data/consumer-rules.pro
--------------------------------------------------------------------------------
/libraries/data/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/libraries/data/src/androidTest/java/com/smarttoolfactory/data/AbstractDaoTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.data
2 |
3 | import androidx.annotation.CallSuper
4 | import androidx.room.Room
5 | import androidx.test.core.app.ApplicationProvider
6 | import com.smarttoolfactory.data.db.PropertyDatabase
7 | import java.util.concurrent.Executors
8 | import org.junit.After
9 | import org.junit.Before
10 |
11 | open class AbstractDaoTest(
12 | private val inMemoryDatabase: Boolean = true,
13 | private val allowMainThreadQueries: Boolean = false,
14 | ) {
15 |
16 | internal lateinit var database: PropertyDatabase
17 |
18 | @CallSuper
19 | @Before
20 | open fun setUp() {
21 |
22 | // using an in-memory database because the information stored here disappears after test
23 | val builder = if (inMemoryDatabase) {
24 | Room.inMemoryDatabaseBuilder(
25 | ApplicationProvider.getApplicationContext(), PropertyDatabase::class.java
26 | )
27 | } else {
28 | Room.databaseBuilder(
29 | ApplicationProvider.getApplicationContext(),
30 | PropertyDatabase::class.java,
31 | "test.db"
32 | )
33 | .fallbackToDestructiveMigration()
34 | }
35 | // 🔥🔥🔥 Without this Coroutines tests with @Transaction get stuck
36 | .setTransactionExecutor(Executors.newSingleThreadExecutor())
37 |
38 | // allowing main thread queries, just for testing
39 | if (allowMainThreadQueries) {
40 | builder.allowMainThreadQueries()
41 | }
42 |
43 | database = builder.build()
44 | }
45 |
46 | @CallSuper
47 | @After
48 | @Throws(Exception::class)
49 | open fun tearDown() {
50 | database.close()
51 | println("🍏 AbstractDaoTest tearDown()")
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/libraries/data/src/androidTest/java/com/smarttoolfactory/data/PropertyDaoTestSuite.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.data
2 |
3 | import org.junit.runner.RunWith
4 | import org.junit.runners.Suite
5 |
6 | // Runs all unit tests with JUnit4.
7 | @RunWith(Suite::class)
8 | @Suite.SuiteClasses(
9 | PropertyDaoCoroutinesTest::class,
10 | PropertyDaoCoroutinesTest::class
11 | )
12 | class PropertyDaoTestSuite
13 |
--------------------------------------------------------------------------------
/libraries/data/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/libraries/data/src/main/java/com/smarttoolfactory/data/api/PropertyApi.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.data.api
2 |
3 | import com.smarttoolfactory.data.constant.ORDER_BY_NONE
4 | import com.smarttoolfactory.data.model.remote.PropertyResponse
5 | import io.reactivex.rxjava3.core.Single
6 | import retrofit2.http.GET
7 | import retrofit2.http.Query
8 |
9 | interface PropertyApiCoroutines {
10 |
11 | @GET("mobileapi/search")
12 | suspend fun getPropertyResponse(
13 | @Query("ob") orderBy: String = ORDER_BY_NONE
14 | ): PropertyResponse
15 |
16 | @GET("mobileapi/search")
17 | suspend fun getPropertyResponseForPage(
18 | @Query("page") page: Int,
19 | @Query("ob") orderBy: String = ORDER_BY_NONE
20 | ): PropertyResponse
21 | }
22 |
23 | interface PropertyApiRxJava {
24 |
25 | @GET("mobileapi/search")
26 | fun getPropertyResponse(
27 | @Query("ob") orderBy: String = ORDER_BY_NONE
28 | ): Single
29 |
30 | @GET("mobileapi/search")
31 | fun getPropertyResponseForPage(
32 | @Query("page") page: Int,
33 | @Query("ob") orderBy: String = ORDER_BY_NONE
34 | ): Single
35 | }
36 |
--------------------------------------------------------------------------------
/libraries/data/src/main/java/com/smarttoolfactory/data/constant/Constants.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.data.constant
2 |
3 | /*
4 | Web Service Constants
5 | */
6 |
7 | // Base Url
8 | const val BASE_URL = "https://www.propertyfinder.ae/"
9 |
10 | // Filter Types
11 | const val KEY_FILTER = "filter-key"
12 | const val ORDER_BY_NONE = ""
13 | const val ORDER_BY_PRICE_ASCENDING = "pa"
14 | const val ORDER_BY_PRICE_DESCENDING = "pd"
15 | const val ORDER_BY_BEDS_ASCENDING = "ba"
16 | const val ORDER_BY_DES_DESCENDING = "bd"
17 |
18 | /*
19 | DBConstants
20 | */
21 | const val DATABASE_NAME = "property.db"
22 | const val DATABASE_VERSION = 4
23 |
--------------------------------------------------------------------------------
/libraries/data/src/main/java/com/smarttoolfactory/data/db/converters/PropertyTypeConverters.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.data.db.converters
2 |
3 | import androidx.room.TypeConverter
4 | import com.google.gson.Gson
5 | import com.google.gson.reflect.TypeToken
6 | import com.smarttoolfactory.data.model.local.BrokerEntity
7 |
8 | class PropertyTypeConverters {
9 |
10 | @TypeConverter
11 | fun fromBrokerEntity(data: BrokerEntity?): String? {
12 | return Gson().toJson(data)
13 | }
14 |
15 | @TypeConverter
16 | fun toBrokerEntity(json: String?): BrokerEntity? {
17 | return Gson().fromJson(json, BrokerEntity::class.java)
18 | }
19 |
20 | @TypeConverter
21 | fun fromStringList(list: List?): String? {
22 | return Gson().toJson(list)
23 | }
24 |
25 | @TypeConverter
26 | fun toStringList(json: String?): List? {
27 | val listType = object : TypeToken>() {}.type
28 | return Gson().fromJson(json, listType)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/libraries/data/src/main/java/com/smarttoolfactory/data/db/dao/PagedPropertyDao.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.data.db.dao
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Delete
5 | import androidx.room.Query
6 | import com.smarttoolfactory.data.model.local.PagedPropertyEntity
7 |
8 | @Dao
9 | interface PagedPropertyDao : BaseCoroutinesDao {
10 |
11 | @Delete
12 | suspend fun deletePagedPropertyEntity(entity: PagedPropertyEntity): Int
13 |
14 | @Query("DELETE FROM paged_property")
15 | suspend fun deleteAll()
16 |
17 | /**
18 | * Get number of properties in db
19 | */
20 | @Query("SELECT COUNT(*) FROM paged_property")
21 | suspend fun getPropertyCount(): Int
22 |
23 | /**
24 | * Get properties from database.
25 | *
26 | * *If database is empty returns empty list []
27 | */
28 | @Query("SELECT * FROM paged_property")
29 | suspend fun getPropertyList(): List
30 | }
31 |
--------------------------------------------------------------------------------
/libraries/data/src/main/java/com/smarttoolfactory/data/db/dao/PropertyCoroutinesDao.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.data.db.dao
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Delete
5 | import androidx.room.Query
6 | import com.smarttoolfactory.data.model.local.PropertyEntity
7 |
8 | @Dao
9 | interface PropertyCoroutinesDao : BaseCoroutinesDao {
10 |
11 | @Delete
12 | suspend fun deletePropertyEntity(entity: PropertyEntity): Int
13 |
14 | @Query("DELETE FROM property")
15 | suspend fun deleteAll()
16 |
17 | /**
18 | * Get number of properties in db
19 | */
20 | @Query("SELECT COUNT(*) FROM property")
21 | suspend fun getPropertyCount(): Int
22 |
23 | /**
24 | * Get properties from database.
25 | *
26 | * *If database is empty returns empty list []
27 | */
28 | @Query("SELECT * FROM property")
29 | suspend fun getPropertyList(): List
30 | }
31 |
--------------------------------------------------------------------------------
/libraries/data/src/main/java/com/smarttoolfactory/data/db/dao/PropertyRxJava3Dao.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.data.db.dao
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Delete
5 | import androidx.room.Insert
6 | import androidx.room.OnConflictStrategy
7 | import androidx.room.Query
8 | import com.smarttoolfactory.data.model.local.PropertyEntity
9 | import io.reactivex.rxjava3.core.Completable
10 | import io.reactivex.rxjava3.core.Maybe
11 | import io.reactivex.rxjava3.core.Observable
12 | import io.reactivex.rxjava3.core.Single
13 |
14 | @Dao
15 | interface PropertyRxJava3Dao : BaseRxDao {
16 |
17 | @Insert(onConflict = OnConflictStrategy.REPLACE)
18 | fun insert(propertyEntity: PropertyEntity): Completable
19 |
20 | @Insert(onConflict = OnConflictStrategy.REPLACE)
21 | fun insert(propertyEntityList: List): Completable
22 |
23 | @Delete
24 | fun deleteProperty(entity: PropertyEntity): Single
25 |
26 | @Query("DELETE FROM property")
27 | fun deleteAll(): Completable
28 |
29 | /**
30 | * Get number of properties in db
31 | */
32 | @Query("SELECT COUNT(*) FROM property")
33 | fun getPropertyCount(): Maybe
34 |
35 | /**
36 | * Get list of [PropertyEntity]s to from database.
37 | */
38 | @Query("SELECT * FROM property")
39 | fun getPropertiesSingle(): Single>
40 |
41 | /**
42 | * Get list of [PropertyEntity]s to from database.
43 | */
44 | @Query("SELECT * FROM property")
45 | fun getPropertiesMaybe(): Maybe>
46 |
47 | /**
48 | * Get list of [PropertyEntity]s to from database.
49 | */
50 | @Query("SELECT * FROM property")
51 | fun getProperties(): Observable>
52 | }
53 |
--------------------------------------------------------------------------------
/libraries/data/src/main/java/com/smarttoolfactory/data/db/dao/SortOrderDAOs.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.data.db.dao
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Insert
5 | import androidx.room.OnConflictStrategy
6 | import androidx.room.Query
7 | import com.smarttoolfactory.data.model.local.SortOrderEntity
8 | import io.reactivex.rxjava3.core.Completable
9 | import io.reactivex.rxjava3.core.Single
10 |
11 | @Dao
12 | interface SortOrderDaoCoroutines {
13 |
14 | @Insert(onConflict = OnConflictStrategy.REPLACE)
15 | suspend fun insert(entity: SortOrderEntity): Long
16 |
17 | @Query("SELECT order_by FROM sort_order")
18 | suspend fun getSortOrderEntity(): String
19 | }
20 |
21 | @Dao
22 | interface SortOrderDaoRxJava3 {
23 |
24 | @Insert(onConflict = OnConflictStrategy.REPLACE)
25 | fun insert(propertyEntity: SortOrderEntity): Completable
26 |
27 | @Query("SELECT order_by FROM sort_order")
28 | fun getSortOrderSingle(): Single
29 | }
30 |
--------------------------------------------------------------------------------
/libraries/data/src/main/java/com/smarttoolfactory/data/db/dao/UserDao.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.data.db.dao
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Query
5 | import com.smarttoolfactory.data.model.local.UserEntity
6 |
7 | /**
8 | * This interface is used for CRUD operation on database
9 | */
10 | @Dao
11 | interface UserDao : BaseCoroutinesDao {
12 |
13 | @Query("SELECT * FROM user")
14 | suspend fun getAllUsers(): List
15 |
16 | @Query("SELECT * FROM user WHERE user.userId =:id")
17 | suspend fun getUserById(id: Long): UserEntity?
18 |
19 | @Query("DELETE FROM user")
20 | suspend fun deleteAll()
21 | }
22 |
--------------------------------------------------------------------------------
/libraries/data/src/main/java/com/smarttoolfactory/data/model/Mappables.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.data.model
2 |
3 | /**
4 | * Interface to create common behavior between entities to be able to use generics to create [Mapper]
5 | */
6 | interface IEntity : Mappable
7 |
8 | /**
9 | * Interface to create common behavior between DTOs to be able to use generics to create [Mapper]
10 | */
11 | interface DataTransferObject : Mappable
12 |
13 | /**
14 | * Marker interface to mark classes that implement, or interfaces that extend this interface
15 | * as convertible from one [Mappable] type to another
16 | */
17 | interface Mappable
18 |
--------------------------------------------------------------------------------
/libraries/data/src/main/java/com/smarttoolfactory/data/model/local/BrokerEntity.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.data.model.local
2 |
3 | import com.smarttoolfactory.data.model.IEntity
4 |
5 | data class BrokerEntity(
6 | val id: Int,
7 | val name: String,
8 | val address: String,
9 | val phone: String,
10 | val phoneExtension: String,
11 | val email: String,
12 |
13 | val mobile: String?,
14 | val agentPhoto: String?,
15 | val agentName: String,
16 |
17 | val leadEmailReceivers: List,
18 | val license: String?,
19 | val agentId: Int,
20 | val logo: String?
21 | ) : IEntity
22 |
--------------------------------------------------------------------------------
/libraries/data/src/main/java/com/smarttoolfactory/data/model/local/PropertyStatus.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.data.model.local
2 |
3 | // @Entity(
4 | // tableName = "favorites",
5 | // indices = [Index(value = ["userAccountId", "postId"])],
6 | // foreignKeys = [
7 | // ForeignKey(
8 | // entity = FavoriteEntity::class,
9 | // parentColumns = ["id"],
10 | // childColumns = ["userAccountId"],
11 | // onDelete = ForeignKey.NO_ACTION
12 | // )
13 | // ]
14 | // )
15 | // data class FavoritesEntities(
16 | // @PrimaryKey(autoGenerate = true)
17 | // val id: Long = 0,
18 | // val userAccountId: Long = NO_ACCOUNT,
19 | // val propertyId: Int,
20 | // val displayCount: Int = 0,
21 | // val favorite: Boolean = false
22 | // )
23 |
--------------------------------------------------------------------------------
/libraries/data/src/main/java/com/smarttoolfactory/data/model/local/SortOrderEntity.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.data.model.local
2 |
3 | import androidx.room.ColumnInfo
4 | import androidx.room.Entity
5 | import androidx.room.PrimaryKey
6 | import com.smarttoolfactory.data.constant.ORDER_BY_NONE
7 |
8 | @Entity(tableName = "sort_order")
9 | data class SortOrderEntity(
10 |
11 | @PrimaryKey
12 | @ColumnInfo(name = "id")
13 | val id: Int = 0,
14 |
15 | @ColumnInfo(name = "order_by", defaultValue = ORDER_BY_NONE)
16 | val orderBy: String = ORDER_BY_NONE
17 | )
18 |
--------------------------------------------------------------------------------
/libraries/data/src/main/java/com/smarttoolfactory/data/model/local/UserEntity.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.data.model.local
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 | import com.smarttoolfactory.data.model.IEntity
6 |
7 | @Entity(tableName = "user")
8 | data class UserEntity(
9 | @PrimaryKey
10 | val userId: Long,
11 | val firstName: String,
12 | val lastName: String,
13 | val email: String,
14 | val password: String
15 | ) : IEntity
16 |
--------------------------------------------------------------------------------
/libraries/data/src/main/java/com/smarttoolfactory/data/model/remote/BrokerDTO.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.data.model.remote
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.smarttoolfactory.data.model.DataTransferObject
5 |
6 | data class BrokerDTO(
7 | @SerializedName("id")
8 | val id: Int,
9 | @SerializedName("name")
10 | val name: String,
11 | @SerializedName("address")
12 | val address: String,
13 | @SerializedName("phone")
14 | val phone: String,
15 | @SerializedName("phone_extension")
16 | val phoneExtension: String,
17 | @SerializedName("email")
18 | val email: String,
19 |
20 | @SerializedName("mobile")
21 | val mobile: String?,
22 |
23 | @SerializedName("agent_photo")
24 | val agentPhoto: String?,
25 | @SerializedName("agent_name")
26 | val agentName: String,
27 |
28 | @SerializedName("lead_email_receivers")
29 | val leadEmailReceivers: List,
30 | @SerializedName("license")
31 | val license: String?,
32 | @SerializedName("agent_id")
33 | val agentId: Int,
34 | @SerializedName("logo")
35 | val logo: String?
36 | ) : DataTransferObject
37 |
--------------------------------------------------------------------------------
/libraries/data/src/main/java/com/smarttoolfactory/data/model/remote/PropertyResponse.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.data.model.remote
2 |
3 | import com.google.gson.annotations.SerializedName
4 | import com.smarttoolfactory.data.model.DataTransferObject
5 |
6 | data class PropertyResponse(
7 | @SerializedName("total")
8 | val total: Int,
9 | @SerializedName("res")
10 | val res: List
11 | ) : DataTransferObject
12 |
--------------------------------------------------------------------------------
/libraries/data/src/test/java/com/smarttoolfactory/data/mapper/PropertyEntityEntityListMapperTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.data.mapper
2 |
3 | import com.google.common.truth.Truth
4 | import com.smarttoolfactory.data.model.remote.PropertyResponse
5 | import com.smarttoolfactory.test_utils.RESPONSE_JSON_PATH
6 | import com.smarttoolfactory.test_utils.util.convertToObjectFromJson
7 | import com.smarttoolfactory.test_utils.util.getResourceAsText
8 | import org.junit.jupiter.api.Test
9 |
10 | class PropertyEntityEntityListMapperTest {
11 |
12 | private val propertyDTOResponse by lazy {
13 | convertToObjectFromJson(getResourceAsText(RESPONSE_JSON_PATH))!!
14 | }
15 |
16 | @Test
17 | fun `given a valid propertyDTO list, should map to propertyEntity list`() {
18 |
19 | val propertyDTOList = propertyDTOResponse.res
20 |
21 | // GIVEN
22 | val mapper = MapperFactory.createListMapper()
23 |
24 | // WHEN
25 | val actual = mapper.map(propertyDTOList)
26 |
27 | // THEN
28 | actual.forEachIndexed { index, propertyEntity ->
29 | Truth.assertThat(propertyEntity.id).isEqualTo(propertyDTOList[index].id)
30 | Truth.assertThat(propertyEntity.update).isEqualTo(propertyDTOList[index].update)
31 | Truth.assertThat(propertyEntity.price).isEqualTo(propertyDTOList[index].price)
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/libraries/domain/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/libraries/domain/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import extension.addUnitTestDependencies
2 |
3 | plugins {
4 | id(Plugins.ANDROID_LIBRARY_PLUGIN)
5 | id(Plugins.KOTLIN_ANDROID_PLUGIN)
6 | id(Plugins.KOTLIN_ANDROID_EXTENSIONS_PLUGIN)
7 | id(Plugins.KOTLIN_KAPT_PLUGIN)
8 | id(Plugins.DAGGER_HILT_PLUGIN)
9 | }
10 |
11 | android {
12 |
13 | compileSdkVersion(AndroidVersion.COMPILE_SDK_VERSION)
14 | defaultConfig {
15 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
16 | }
17 |
18 | buildTypes {
19 | getByName("release") {
20 | isMinifyEnabled = false
21 | proguardFiles(
22 | getDefaultProguardFile("proguard-android-optimize.txt"),
23 | "proguard-rules.pro"
24 | )
25 | }
26 | }
27 | compileOptions {
28 | sourceCompatibility = JavaVersion.VERSION_1_8
29 | targetCompatibility = JavaVersion.VERSION_1_8
30 | }
31 | kotlinOptions {
32 | jvmTarget = "1.8"
33 | }
34 | }
35 |
36 | dependencies {
37 |
38 | implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
39 |
40 | implementation(project(Modules.AndroidLibrary.DATA))
41 |
42 | implementation(Deps.KOTLIN)
43 | implementation(Deps.ANDROIDX_CORE_KTX)
44 |
45 | // Dagger
46 | implementation(Deps.DAGGER_HILT_ANDROID)
47 | kapt(Deps.DAGGER_HILT_COMPILER)
48 |
49 | // RxJava
50 | implementation(Deps.RX_JAVA3)
51 | // RxAndroid
52 | implementation(Deps.RX_JAVA3_ANDROID)
53 |
54 | // Coroutines
55 | implementation(Deps.COROUTINES_CORE)
56 | implementation(Deps.COROUTINES_ANDROID)
57 |
58 | testImplementation(Deps.GSON)
59 |
60 | addUnitTestDependencies()
61 | testImplementation(project(Modules.AndroidLibrary.TEST_UTILS))
62 | }
63 |
--------------------------------------------------------------------------------
/libraries/domain/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/PropertyFindAR/5d2b510a8d1c489a5520b1fff6a143552e50ca79/libraries/domain/consumer-rules.pro
--------------------------------------------------------------------------------
/libraries/domain/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/libraries/domain/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/libraries/domain/src/main/java/com/smarttoolfactory/domain/Constants.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.domain
2 |
3 | const val ORDER_BY_NONE = com.smarttoolfactory.data.constant.ORDER_BY_NONE
4 | const val ORDER_BY_PRICE_ASCENDING = com.smarttoolfactory.data.constant.ORDER_BY_PRICE_ASCENDING
5 | const val ORDER_BY_PRICE_DESCENDING = com.smarttoolfactory.data.constant.ORDER_BY_PRICE_DESCENDING
6 | const val ORDER_BY_BEDS_ASCENDING = com.smarttoolfactory.data.constant.ORDER_BY_BEDS_ASCENDING
7 | const val ORDER_BY_DES_DESCENDING = com.smarttoolfactory.data.constant.ORDER_BY_DES_DESCENDING
8 |
--------------------------------------------------------------------------------
/libraries/domain/src/main/java/com/smarttoolfactory/domain/base/Disposable.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.domain.base
2 |
3 | /**
4 | * Interface for adding common behavior for any class that has items that need to be disposed
5 | * such as RxJava Observables or Coroutines jobs.
6 | */
7 | interface Disposable {
8 |
9 | fun dispose()
10 | }
11 |
--------------------------------------------------------------------------------
/libraries/domain/src/main/java/com/smarttoolfactory/domain/dispatcher/UseCaseDispatchers.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.domain.dispatcher
2 |
3 | import io.reactivex.rxjava3.core.Observable
4 | import kotlinx.coroutines.CoroutineDispatcher
5 | import kotlinx.coroutines.Dispatchers
6 | import kotlinx.coroutines.flow.Flow
7 |
8 | /**
9 | * Class for providing dispatcher for different thread operations. This class is useful
10 | * in tests to control every thread in one place.
11 | *
12 | * Multiple flowOn operators fuse to a single flowOn with a combined context.
13 | * The elements of the context of the first flowOn operator naturally take precedence over
14 | *
15 | * the elements of the second flowOn operator when they have the same context keys, for example:
16 | * ```
17 | * flow.map { ... } // Will be executed in IO
18 | * .flowOn(Dispatchers.IO) // This one takes precedence
19 | * .flowOn(Dispatchers.Default)
20 | * ```
21 | *
22 | * ### Note: While [Observable.observeOn] runs down stream, [Flow.flowOn] works upstream
23 | */
24 |
25 | data class UseCaseDispatchers(
26 | val ioDispatcher: CoroutineDispatcher = Dispatchers.IO,
27 | val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default,
28 | val mainDispatcher: CoroutineDispatcher = Dispatchers.Main,
29 | )
30 |
--------------------------------------------------------------------------------
/libraries/domain/src/main/java/com/smarttoolfactory/domain/error/EmptyDataException.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.domain.error
2 |
3 | /**
4 | * Exception for indicating there is no data retrieved from remote or local source
5 | */
6 | class EmptyDataException(message: String) : Exception(message)
7 |
--------------------------------------------------------------------------------
/libraries/domain/src/main/java/com/smarttoolfactory/domain/model/BrokerItem.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.domain.model
2 |
3 | import android.os.Parcelable
4 | import kotlinx.android.parcel.Parcelize
5 |
6 | @Parcelize
7 | data class BrokerItem(
8 | val id: Int,
9 | val name: String,
10 | val address: String,
11 | val phone: String,
12 | val phoneExtension: String,
13 | val email: String,
14 |
15 | val mobile: String?,
16 | val agentPhoto: String?,
17 | val agentName: String,
18 |
19 | val leadEmailReceivers: List,
20 | val license: String?,
21 | val agentId: Int,
22 | val logo: String?
23 | ) : Item, Parcelable
24 |
--------------------------------------------------------------------------------
/libraries/domain/src/main/java/com/smarttoolfactory/domain/model/Item.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.domain.model
2 |
3 | import android.os.Parcelable
4 | import com.smarttoolfactory.data.model.Mappable
5 |
6 | interface Item : Parcelable, Mappable {
7 |
8 | /**
9 | * Data classes have same hash code if their constructor has the same objects
10 | *
11 | * * Data classes have 3 common properties if properties of constructor is the same
12 | * * Hash code
13 | * * Equals
14 | * * toString
15 | */
16 | fun dataEquals(other: Item?): Boolean {
17 | return this.hashCode() == other.hashCode()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/libraries/domain/src/main/java/com/smarttoolfactory/domain/model/PropertyChartItem.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.domain.model
2 |
3 | data class PropertyChartItem(
4 | val id: Int,
5 | val title: String,
6 | val price: Float,
7 | val currency: String,
8 | private val beds: String = "",
9 | private val baths: String = "",
10 | val location: String
11 | ) {
12 |
13 | val bedRooms = try {
14 | beds.toInt()
15 | } catch (e: NumberFormatException) {
16 | -1
17 | }
18 |
19 | val bathRooms = try {
20 | baths.toInt()
21 | } catch (e: NumberFormatException) {
22 | -1
23 | }
24 |
25 | var position: Int = 0
26 | }
27 |
--------------------------------------------------------------------------------
/libraries/domain/src/main/java/com/smarttoolfactory/domain/model/PropertyItem.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.domain.model
2 |
3 | import android.os.Parcelable
4 | import kotlinx.android.parcel.Parcelize
5 |
6 | /**
7 | * UI item for Properties
8 | */
9 | @Parcelize
10 | data class PropertyItem(
11 |
12 | val id: Int,
13 | val update: Int,
14 | val categoryId: Int,
15 | val title: String,
16 | val subject: String,
17 | val type: String,
18 | val typeId: Int,
19 | val thumbnail: String?,
20 | val thumbnailBig: String?,
21 | val imageCount: Int,
22 | val price: String,
23 | val pricePeriod: String?,
24 | val pricePeriodRaw: String,
25 | val priceLabel: String?,
26 | val priceValue: String?,
27 | val priceValueRaw: Int,
28 | val currency: String,
29 | val featured: Boolean,
30 | val location: String,
31 | val area: String,
32 | val poa: Boolean,
33 | val reraPermit: String?,
34 | val bathrooms: String,
35 | val bedrooms: String,
36 | val dateInsert: String,
37 | val dateUpdate: String,
38 | val agentName: String,
39 | val brokerName: String,
40 | val agentLicense: String?,
41 | val locationId: Int,
42 | val hideLocation: Boolean,
43 |
44 | val broker: BrokerItem,
45 | val amenities: List,
46 | val amenitiesKeys: List,
47 |
48 | val latitude: Double,
49 | val longitude: Double,
50 | val premium: Boolean,
51 | val livingrooms: String,
52 | val verified: Boolean,
53 |
54 | val gallery: List?,
55 | val phone: String,
56 |
57 | val leadEmailReceivers: List,
58 |
59 | val reference: String,
60 |
61 | var viewCount: Int = 0,
62 | var isFavorite: Boolean = false,
63 | /*
64 | This should be a unique transition id for to have shared transitions between fragments.
65 | For multiple RecyclerViews with same items it should have prefix to separate views.
66 | */
67 | var transitionName: String = id.toString()
68 | ) : Parcelable
69 |
--------------------------------------------------------------------------------
/libraries/domain/src/test/java/com/smarttoolfactory/domain/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.domain
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/libraries/domain/src/test/java/com/smarttoolfactory/domain/mapper/PropertyEntityToItemListMapperTest.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.domain.mapper
2 |
3 | import com.smarttoolfactory.data.mapper.MapperFactory
4 | import com.smarttoolfactory.data.mapper.PropertyDTOtoEntityListMapper
5 | import com.smarttoolfactory.data.model.remote.PropertyResponse
6 | import com.smarttoolfactory.test_utils.RESPONSE_JSON_PATH
7 | import com.smarttoolfactory.test_utils.util.convertToObjectFromJson
8 | import com.smarttoolfactory.test_utils.util.getResourceAsText
9 |
10 | class PropertyEntityToItemListMapperTest {
11 |
12 | companion object {
13 |
14 | private val propertyDTOList by lazy {
15 | convertToObjectFromJson(getResourceAsText(RESPONSE_JSON_PATH))!!.res
16 | }
17 |
18 | private val entityList =
19 | MapperFactory.createListMapper()
20 | .map(propertyDTOList)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/libraries/domain/src/test/java/com/smarttoolfactory/domain/usecase/TestData.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.domain.usecase
2 |
3 | import com.smarttoolfactory.data.mapper.MapperFactory
4 | import com.smarttoolfactory.data.mapper.PropertyDTOtoEntityListMapper
5 | import com.smarttoolfactory.data.model.remote.PropertyResponse
6 | import com.smarttoolfactory.domain.mapper.PropertyEntityToItemListMapper
7 | import com.smarttoolfactory.test_utils.RESPONSE_JSON_PATH
8 | import com.smarttoolfactory.test_utils.RESPONSE_JSON_PATH_PAGE_1
9 | import com.smarttoolfactory.test_utils.RESPONSE_JSON_PATH_PAGE_2
10 | import com.smarttoolfactory.test_utils.util.convertToObjectFromJson
11 | import com.smarttoolfactory.test_utils.util.getResourceAsText
12 |
13 | object TestData {
14 |
15 | /*
16 | DTOs
17 | */
18 | private val propertyResponse = convertToObjectFromJson(
19 | getResourceAsText(RESPONSE_JSON_PATH)
20 | )!!
21 |
22 | private val propertyDTOList = propertyResponse.res
23 |
24 | private val propertyResponsePage1 = convertToObjectFromJson(
25 | getResourceAsText(RESPONSE_JSON_PATH_PAGE_1)
26 | )!!
27 |
28 | private val propertyResponsePage2 = convertToObjectFromJson(
29 | getResourceAsText(RESPONSE_JSON_PATH_PAGE_2)
30 | )!!
31 |
32 | /*
33 | Properties
34 | */
35 | private val entityMapper = MapperFactory.createListMapper()
36 |
37 | val entityList = entityMapper.map(propertyDTOList)
38 | val entityListPage1 = entityMapper.map(propertyResponsePage1.res)
39 | val entityListPage2 = entityMapper.map(propertyResponsePage2.res)
40 |
41 | /*
42 | Items
43 | */
44 | val itemMapper = MapperFactory.createListMapper()
45 |
46 | val itemList = itemMapper.map(entityList)
47 | val itemListPage1 = itemMapper.map(entityListPage1)
48 | val itemListPage2 = itemMapper.map(entityListPage2)
49 | }
50 |
--------------------------------------------------------------------------------
/libraries/test-utils/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/libraries/test-utils/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SmartToolFactory/PropertyFindAR/5d2b510a8d1c489a5520b1fff6a143552e50ca79/libraries/test-utils/consumer-rules.pro
--------------------------------------------------------------------------------
/libraries/test-utils/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.kts.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/libraries/test-utils/src/androidTest/java/com/smarttoolfactory/test_utils/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | // package com.smarttoolfactory.test_utils
2 | //
3 | // import androidx.test.ext.junit.runners.AndroidJUnit4
4 | // import androidx.test.platform.app.InstrumentationRegistry
5 | // import org.junit.Test
6 | // import org.junit.runner.RunWith
7 | //
8 | // /**
9 | // * Instrumented test, which will execute on an Android device.
10 | // *
11 | // * See [testing documentation](http://d.android.com/tools/testing).
12 | // */
13 | // @RunWith(AndroidJUnit4::class)
14 | // class ExampleInstrumentedTest {
15 | // @Test
16 | // fun useAppContext() {
17 | // // Context of the app under test.
18 | // val appContext = InstrumentationRegistry.getInstrumentation().targetContext
19 | // assertEquals("com.smarttoolfactory.test_shared.test", appContext.packageName)
20 | // }
21 | // }
22 |
--------------------------------------------------------------------------------
/libraries/test-utils/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/libraries/test-utils/src/main/java/com/smarttoolfactory/test_utils/TestConstans.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.test_utils
2 |
3 | const val RESPONSE_JSON_PATH = "response_sort_by_none.json"
4 | const val RESPONSE_JSON_PATH_ORDER_BY_PRICE_ASCENDING = "response_sort_by_pa.json"
5 | const val RESPONSE_JSON_PATH_ORDER_BY_PRICE_DESCENDING = "response_sort_by_pd.json"
6 | const val RESPONSE_JSON_PATH_ORDER_BY_BEDS_ASCENDING = "response_sort_by_ba.json"
7 | const val RESPONSE_JSON_PATH_ORDER_BY_BEDS_DESCENDING = "response_sort_by_bd.json"
8 | const val RESPONSE_JSON_PATH_PAGE_1 = "response_page1.json"
9 | const val RESPONSE_JSON_PATH_PAGE_2 = "response_page2.json"
10 | const val RESPONSE_JSON_PATH_PAGE_3 = "response_page3.json"
11 | const val SERVER_INTERNAL_ERROR_MESSAGE = "Unexpected error occurred"
12 |
--------------------------------------------------------------------------------
/libraries/test-utils/src/main/java/com/smarttoolfactory/test_utils/extension/RxImmediateSchedulerExtension.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.test_utils.extension
2 |
3 | import io.reactivex.rxjava3.android.plugins.RxAndroidPlugins
4 | import io.reactivex.rxjava3.core.Scheduler
5 | import io.reactivex.rxjava3.internal.schedulers.ExecutorScheduler
6 | import io.reactivex.rxjava3.plugins.RxJavaPlugins
7 | import java.util.concurrent.Executor
8 | import org.junit.jupiter.api.extension.AfterEachCallback
9 | import org.junit.jupiter.api.extension.BeforeEachCallback
10 | import org.junit.jupiter.api.extension.ExtensionContext
11 |
12 | /**
13 | * LifeCycle
14 | *
15 | * * BeforeAllCallback
16 | * * BeforeAll
17 | * * BeforeEachCallback
18 | * * BeforeEach
19 | * * BeforeTestExecutionCallback
20 | * * Test
21 | * * AfterTestExecutionCallback
22 | * * AfterEach
23 | * * AfterEachCallback
24 | * * AfterAll
25 | * * AfterAllCallback
26 | */
27 | class RxImmediateSchedulerExtension : BeforeEachCallback, AfterEachCallback {
28 |
29 | private val immediate = object : Scheduler() {
30 |
31 | override fun createWorker(): Worker {
32 | return ExecutorScheduler.ExecutorWorker(Executor { it.run() }, true, true)
33 | }
34 | }
35 |
36 | // private val immediate = Schedulers.trampoline()
37 |
38 | override fun beforeEach(context: ExtensionContext?) {
39 | RxJavaPlugins.setInitIoSchedulerHandler { immediate }
40 | RxJavaPlugins.setInitComputationSchedulerHandler { immediate }
41 | RxJavaPlugins.setInitNewThreadSchedulerHandler { immediate }
42 | RxJavaPlugins.setInitSingleSchedulerHandler { immediate }
43 | RxAndroidPlugins.setInitMainThreadSchedulerHandler { immediate }
44 | }
45 |
46 | override fun afterEach(context: ExtensionContext?) {
47 | RxJavaPlugins.reset()
48 | RxAndroidPlugins.reset()
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/libraries/test-utils/src/main/java/com/smarttoolfactory/test_utils/extension/TestCoroutineExtension.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.test_utils.extension
2 |
3 | import kotlinx.coroutines.Dispatchers
4 | import kotlinx.coroutines.test.TestCoroutineDispatcher
5 | import kotlinx.coroutines.test.TestCoroutineScope
6 | import kotlinx.coroutines.test.resetMain
7 | import kotlinx.coroutines.test.runBlockingTest
8 | import kotlinx.coroutines.test.setMain
9 | import org.junit.jupiter.api.extension.AfterEachCallback
10 | import org.junit.jupiter.api.extension.BeforeEachCallback
11 | import org.junit.jupiter.api.extension.ExtensionContext
12 |
13 | /**
14 | * LifeCycle
15 | *
16 | * * BeforeAllCallback
17 | * * BeforeAll
18 | * * BeforeEachCallback
19 | * * BeforeEach
20 | * * BeforeTestExecutionCallback
21 | * * Test
22 | * * AfterTestExecutionCallback
23 | * * AfterEach
24 | * * AfterEachCallback
25 | * * AfterAll
26 | * * AfterAllCallback
27 | */
28 | class TestCoroutineExtension : BeforeEachCallback, AfterEachCallback {
29 |
30 | private val testCoroutineDispatcher = TestCoroutineDispatcher()
31 |
32 | val testCoroutineScope = TestCoroutineScope(testCoroutineDispatcher)
33 |
34 | override fun beforeEach(context: ExtensionContext?) {
35 | println("🚙 TestCoroutineExtension beforeEach()")
36 | Dispatchers.setMain(testCoroutineDispatcher)
37 | }
38 |
39 | override fun afterEach(context: ExtensionContext?) {
40 |
41 | println("🚗 TestCoroutineExtension afterEach()")
42 |
43 | Dispatchers.resetMain()
44 | try {
45 | testCoroutineScope.cleanupTestCoroutines()
46 | } catch (exception: Exception) {
47 | exception.printStackTrace()
48 | }
49 | }
50 |
51 | fun runBlockingTest(block: suspend TestCoroutineScope.() -> Unit) =
52 | testCoroutineScope.runBlockingTest { block() }
53 | }
54 |
--------------------------------------------------------------------------------
/libraries/test-utils/src/main/java/com/smarttoolfactory/test_utils/rule/MockWebServerRule.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.test_utils.rule
2 |
3 | import okhttp3.mockwebserver.MockWebServer
4 | import org.junit.rules.TestRule
5 | import org.junit.runner.Description
6 | import org.junit.runners.model.Statement
7 |
8 | /**
9 | * Test rule for JUnit4 to invoke actions which are
10 | * start [MockWebServer],
11 | * run the test ,
12 | * and shut [MockWebServer] down after the test is run.
13 | */
14 | class MockWebServerRule : TestRule {
15 |
16 | val mockWebServer = MockWebServer()
17 |
18 | override fun apply(
19 | base: Statement,
20 | description: Description
21 | ): Statement {
22 |
23 | return object : Statement() {
24 |
25 | @Throws(Throwable::class)
26 | override fun evaluate() {
27 | mockWebServer.start()
28 | base.evaluate()
29 | mockWebServer.shutdown()
30 | }
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/libraries/test-utils/src/main/java/com/smarttoolfactory/test_utils/rule/RxImmediateSchedulerRule.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.test_utils.rule
2 |
3 | import io.reactivex.rxjava3.android.plugins.RxAndroidPlugins
4 | import io.reactivex.rxjava3.core.Scheduler
5 | import io.reactivex.rxjava3.internal.schedulers.ExecutorScheduler
6 | import io.reactivex.rxjava3.plugins.RxJavaPlugins
7 | import java.util.concurrent.Executor
8 | import org.junit.rules.TestRule
9 | import org.junit.runner.Description
10 | import org.junit.runners.model.Statement
11 |
12 | class RxImmediateSchedulerRule : TestRule {
13 |
14 | private val immediate = object : Scheduler() {
15 |
16 | override fun createWorker(): Worker {
17 | return ExecutorScheduler.ExecutorWorker(Executor { it.run() }, true, true)
18 | }
19 | }
20 |
21 | // private val immediate = Schedulers.trampoline()
22 |
23 | override fun apply(base: Statement, description: Description): Statement {
24 | return object : Statement() {
25 | @Throws(Throwable::class)
26 | override fun evaluate() {
27 | RxJavaPlugins.setInitIoSchedulerHandler { immediate }
28 | RxJavaPlugins.setInitComputationSchedulerHandler { immediate }
29 | RxJavaPlugins.setInitNewThreadSchedulerHandler { immediate }
30 | RxJavaPlugins.setInitSingleSchedulerHandler { immediate }
31 | RxAndroidPlugins.setInitMainThreadSchedulerHandler { immediate }
32 |
33 | try {
34 | base.evaluate()
35 | } finally {
36 | RxJavaPlugins.reset()
37 | RxAndroidPlugins.reset()
38 | }
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/libraries/test-utils/src/main/java/com/smarttoolfactory/test_utils/rule/TestCoroutineRule.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.test_utils.rule
2 |
3 | import kotlinx.coroutines.Dispatchers
4 | import kotlinx.coroutines.ExperimentalCoroutinesApi
5 | import kotlinx.coroutines.test.TestCoroutineDispatcher
6 | import kotlinx.coroutines.test.TestCoroutineScope
7 | import kotlinx.coroutines.test.resetMain
8 | import kotlinx.coroutines.test.runBlockingTest
9 | import kotlinx.coroutines.test.setMain
10 | import org.junit.rules.TestRule
11 | import org.junit.runner.Description
12 | import org.junit.runners.model.Statement
13 |
14 | @ExperimentalCoroutinesApi
15 | class TestCoroutineRule : TestRule {
16 |
17 | private val testCoroutineDispatcher = TestCoroutineDispatcher()
18 |
19 | val testCoroutineScope = TestCoroutineScope(testCoroutineDispatcher)
20 |
21 | override fun apply(base: Statement, description: Description?) = object : Statement() {
22 |
23 | @Throws(Throwable::class)
24 | override fun evaluate() {
25 |
26 | Dispatchers.setMain(testCoroutineDispatcher)
27 |
28 | base.evaluate()
29 |
30 | Dispatchers.resetMain()
31 | try {
32 | testCoroutineScope.cleanupTestCoroutines()
33 | } catch (exception: Exception) {
34 | exception.printStackTrace()
35 | }
36 | }
37 | }
38 |
39 | fun runBlockingTest(block: suspend TestCoroutineScope.() -> Unit) =
40 | testCoroutineScope.runBlockingTest { block() }
41 | }
42 |
--------------------------------------------------------------------------------
/libraries/test-utils/src/main/java/com/smarttoolfactory/test_utils/util/LiveDataTestUtil.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.test_utils.util
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.Observer
5 | import java.util.concurrent.CountDownLatch
6 | import java.util.concurrent.TimeUnit
7 | import java.util.concurrent.TimeoutException
8 |
9 | /**
10 | * Gets the value of a [LiveData] or waits for it to have one, with a timeout.
11 | *
12 | * Use this extension from host-side (JVM) tests. It's recommended to use it alongside
13 | * `InstantTaskExecutorRule` or a similar mechanism to execute tasks synchronously.
14 | */
15 | fun LiveData.getOrAwaitValue(
16 | time: Long = 2,
17 | timeUnit: TimeUnit = TimeUnit.SECONDS,
18 | afterObserve: () -> Unit = {}
19 | ): T {
20 |
21 | var data: T? = null
22 | val latch = CountDownLatch(1)
23 |
24 | val observer = object : Observer {
25 | override fun onChanged(o: T?) {
26 | data = o
27 | latch.countDown()
28 | this@getOrAwaitValue.removeObserver(this)
29 | }
30 | }
31 |
32 | this.observeForever(observer)
33 |
34 | afterObserve.invoke()
35 |
36 | // Don't wait indefinitely if the LiveData is not set.
37 | if (!latch.await(time, timeUnit)) {
38 | this.removeObserver(observer)
39 | throw TimeoutException("LiveData value was never set.")
40 | }
41 |
42 | @Suppress("UNCHECKED_CAST")
43 | return data as T
44 | }
45 |
--------------------------------------------------------------------------------
/libraries/test-utils/src/main/java/com/smarttoolfactory/test_utils/util/ReadResourceUtil.kt:
--------------------------------------------------------------------------------
1 | package com.smarttoolfactory.test_utils.util
2 |
3 | import com.google.gson.Gson
4 | import com.google.gson.GsonBuilder
5 | import com.google.gson.internal.LinkedTreeMap
6 | import com.google.gson.reflect.TypeToken
7 |
8 | /**
9 | * Use this method to get json files as string from resources folder to use in tests.
10 | */
11 | fun getResourceAsText(path: String): String {
12 | return object {}.javaClass.classLoader!!.getResource(path)!!.readText()
13 | }
14 |
15 | inline fun Gson.fromJsonWithType(json: String): T? =
16 | fromJson(json, object : TypeToken() {}.type)
17 |
18 | /**
19 | *
20 | * Convert from json to item with type T
21 | *
22 | * * This function returns for some items as [LinkedTreeMap]
23 | */
24 | inline fun convertToObjectFromJson(json: String): T? {
25 | return Gson().fromJsonWithType(json)
26 | }
27 |
28 | /**
29 | *
30 | * Convert from json to [List] of items with type T
31 | *
32 | * * This function returns for some items as [LinkedTreeMap]
33 | */
34 | inline fun fromJsonToListOf(json: String): List {
35 | return GsonBuilder().create().fromJson(json, Array::class.java).asList()
36 | }
37 |
38 | fun Gson.mapFromLinkedTreeMap(map: Map?, type: Class): T? {
39 | if (map == null) return null
40 |
41 | val json = toJson(map)
42 | return fromJson(json, type)
43 | }
44 |
45 | inline fun convertFromJsonToListOf(json: String): List? {
46 |
47 | val gson = GsonBuilder().create()
48 |
49 | val itemList = fromJsonToListOf(json)
50 |
51 | if (itemList.first() !is LinkedTreeMap<*, *>)
52 | return itemList
53 |
54 | // Must use map here because the result is a list of LinkedTreeMaps
55 | val list: ArrayList