Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit e15fd69

Browse files
Merge branch 'release/v0.8.0'
2 parents d411da4 + a8fb0f7 commit e15fd69

File tree

38 files changed

+871
-36
lines changed

38 files changed

+871
-36
lines changed

‎app/build.gradle.kts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,19 @@ android {
7777
// }
7878
// }
7979

80+
// configurations.all {
81+
// resolutionStrategy {
82+
// exclude("org.jetbrains.kotlinx", "kotlinx-coroutines-debug")
83+
// }
84+
// }
85+
86+
packagingOptions {
87+
exclude("**/attach_hotspot_windows.dll")
88+
exclude("META-INF/licenses/**")
89+
exclude("META-INF/AL2.0")
90+
exclude("META-INF/LGPL2.1")
91+
}
92+
8093
android.buildFeatures.dataBinding = true
8194

8295
compileOptions {
@@ -87,6 +100,7 @@ android {
87100
kotlinOptions {
88101
jvmTarget = "1.8"
89102
}
103+
dynamicFeatures = mutableSetOf(Modules.DynamicFM.POST_DETAIL)
90104
}
91105

92106
dependencies {
@@ -96,14 +110,16 @@ dependencies {
96110
implementation(project(Modules.AndroidLibrary.CORE))
97111

98112
implementation(project(Modules.AndroidLibrary.DOMAIN))
99-
// TODO Solve Why doesn't work when DATA module is not added?
113+
// TODO Solve Why doesn't work when DATA module is not added to dagger Hilt?
100114
implementation(project(Modules.AndroidLibrary.DATA))
101115

102116
addAppModuleDependencies()
103117

118+
// Unit Tests
104119
addUnitTestDependencies()
105120
testImplementation(project(Modules.AndroidLibrary.TEST_UTILS))
106121

122+
// Instrumentation Tests
107123
addInstrumentationTestDependencies()
108124
androidTestImplementation(project(Modules.AndroidLibrary.TEST_UTILS))
109125
}

‎app/src/androidTest/java/com/smarttoolfactory/postdynamichilt/ExampleInstrumentedTest.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ package com.smarttoolfactory.postdynamichilt
22

33
import androidx.test.ext.junit.runners.AndroidJUnit4
44
import androidx.test.platform.app.InstrumentationRegistry
5+
import com.smarttoolfactory.domain.model.Post
6+
import com.smarttoolfactory.test_utils.RESPONSE_JSON_PATH
7+
import com.smarttoolfactory.test_utils.util.convertFromJsonToListOf
8+
import com.smarttoolfactory.test_utils.util.getResourceAsText
59
import junit.framework.TestCase.assertEquals
610
import org.junit.Test
711
import org.junit.runner.RunWith
@@ -13,6 +17,14 @@ import org.junit.runner.RunWith
1317
*/
1418
@RunWith(AndroidJUnit4::class)
1519
class ExampleInstrumentedTest {
20+
21+
private val postList =
22+
convertFromJsonToListOf<Post>(getResourceAsText(RESPONSE_JSON_PATH))!!
23+
24+
@Test
25+
fun testExtensionFunctions() {
26+
assertEquals(postList.size, 100)
27+
}
1628
@Test
1729
fun useAppContext() {
1830
// Context of the app under test.
Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,19 @@
11
package com.smarttoolfactory.postdynamichilt
22

33
import android.os.Bundle
4-
import androidx.activity.viewModels
54
import androidx.appcompat.app.AppCompatActivity
6-
import androidx.lifecycle.Observer
75
import com.smarttoolfactory.domain.usecase.GetPostListUseCaseFlow
8-
import com.smarttoolfactory.postdynamichilt.postlist.PostListViewModelRxJava3
96
import dagger.hilt.android.AndroidEntryPoint
107
import javax.inject.Inject
118

129
@AndroidEntryPoint
1310
class MainActivity : AppCompatActivity() {
1411

15-
// private val viewModel: PostListViewModel by viewModels()
16-
private val viewModel: PostListViewModelRxJava3 by viewModels()
17-
1812
@Inject
1913
lateinit var getPostListUseCaseFlow: GetPostListUseCaseFlow
2014

2115
override fun onCreate(savedInstanceState: Bundle?) {
2216
super.onCreate(savedInstanceState)
2317
setContentView(R.layout.activity_main)
24-
viewModel.getPosts()
25-
26-
viewModel.postViewState.observe(
27-
this,
28-
Observer {
29-
println("🔥 MainActivity view state: ${it.status}")
30-
}
31-
)
3218
}
3319
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package com.smarttoolfactory.postdynamichilt
2+
3+
import android.view.View
4+
import android.widget.ImageView
5+
import androidx.databinding.BindingAdapter
6+
import androidx.recyclerview.widget.ListAdapter
7+
import androidx.recyclerview.widget.RecyclerView
8+
import com.bumptech.glide.Glide
9+
import com.bumptech.glide.request.RequestOptions
10+
import com.smarttoolfactory.domain.model.Post
11+
12+
/*
13+
*** Bindings for RecyclerView ***
14+
*/
15+
16+
/**
17+
* [BindingAdapter]s for the [Post]s to ListAdapter.
18+
*/
19+
@BindingAdapter("app:items")
20+
fun RecyclerView.setItems(items: List<Post>?) {
21+
22+
items?.let {
23+
(adapter as ListAdapter<Post, *>)?.submitList(items)
24+
}
25+
}
26+
27+
/**
28+
* Binding adapter used with this class android:src used with binding of this object
29+
* loads image from url into specified view
30+
*
31+
* @param view image to be loaded into
32+
* @param path of the image to be fetched
33+
*/
34+
@BindingAdapter("imageSrc")
35+
fun setImageUrl(view: ImageView, userId: Int) {
36+
37+
try {
38+
39+
val modulus = 6
40+
41+
val drawableRes = when {
42+
userId % modulus == 0 -> {
43+
R.drawable.avatar_1_raster
44+
}
45+
userId % modulus == 1 -> {
46+
R.drawable.avatar_2_raster
47+
}
48+
userId % modulus == 2 -> {
49+
R.drawable.avatar_3_raster
50+
}
51+
userId % modulus == 3 -> {
52+
R.drawable.avatar_4_raster
53+
}
54+
userId % modulus == 4 -> {
55+
R.drawable.avatar_5_raster
56+
}
57+
else -> {
58+
R.drawable.avatar_6_raster
59+
}
60+
}
61+
62+
val requestOptions = RequestOptions()
63+
requestOptions.placeholder(R.drawable.ic_launcher_background)
64+
65+
Glide
66+
.with(view.context)
67+
.setDefaultRequestOptions(requestOptions)
68+
.load(drawableRes)
69+
.into(view)
70+
} catch (e: Exception) {
71+
e.printStackTrace()
72+
}
73+
}
74+
75+
/**
76+
* Display or hide a view based on a condition
77+
*/
78+
@BindingAdapter("visibilityBasedOn")
79+
fun View.visibilityBasedOn(condition: Boolean) {
80+
visibility = if (condition) View.VISIBLE else View.GONE
81+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package com.smarttoolfactory.postdynamichilt.postlist
2+
3+
import android.view.LayoutInflater
4+
import android.view.ViewGroup
5+
import androidx.annotation.LayoutRes
6+
import androidx.databinding.DataBindingUtil
7+
import androidx.databinding.ViewDataBinding
8+
import androidx.recyclerview.widget.DiffUtil
9+
import androidx.recyclerview.widget.ListAdapter
10+
import androidx.recyclerview.widget.RecyclerView
11+
import com.smarttoolfactory.domain.model.Post
12+
import com.smarttoolfactory.postdynamichilt.BR
13+
14+
class PostListAdapter(
15+
16+
@LayoutRes private val layoutId: Int,
17+
private val onItemClicked: ((Post) -> Unit)? = null
18+
19+
) :
20+
ListAdapter<Post, PostListAdapter.CustomViewHolder<Post>>(
21+
PostDiffCallback()
22+
) {
23+
24+
override fun onCreateViewHolder(
25+
parent: ViewGroup,
26+
viewType: Int
27+
): CustomViewHolder<Post> {
28+
29+
val binding =
30+
DataBindingUtil.inflate<ViewDataBinding>(
31+
LayoutInflater.from(parent.context),
32+
layoutId,
33+
parent,
34+
false
35+
)
36+
37+
return CustomViewHolder<Post>(
38+
binding
39+
)
40+
.apply {
41+
onViewHolderCreated(this, binding)
42+
}
43+
}
44+
45+
/**
46+
* Add click listener here to prevent setting listener after a ViewHolder every time
47+
* ViewHolder is scrolled and onBindViewHolder is called
48+
*/
49+
private fun onViewHolderCreated(
50+
viewHolder: RecyclerView.ViewHolder,
51+
binding: ViewDataBinding
52+
) {
53+
54+
binding.root.setOnClickListener {
55+
onItemClicked?.let {
56+
it((getItem(viewHolder.bindingAdapterPosition)))
57+
}
58+
}
59+
}
60+
61+
override fun onBindViewHolder(holder: CustomViewHolder<Post>, position: Int) {
62+
val item = getItem(position)
63+
holder.bindTo(item)
64+
}
65+
66+
class CustomViewHolder<T> constructor(
67+
private val binding: ViewDataBinding
68+
) :
69+
RecyclerView.ViewHolder(binding.root) {
70+
71+
fun bindTo(
72+
item: T
73+
) {
74+
// Bind item to layout to dispatch data to layout
75+
binding.setVariable(BR.item, item)
76+
binding.executePendingBindings()
77+
}
78+
}
79+
}
80+
81+
/**
82+
* Callback for calculating the diff between two non-null items in a list.
83+
*
84+
* Used by ListAdapter to calculate the minimum number of changes between and old list and a new
85+
* list that's been passed to `submitList`.
86+
*/
87+
class PostDiffCallback : DiffUtil.ItemCallback<Post>() {
88+
89+
override fun areItemsTheSame(
90+
oldItem: Post,
91+
newItem: Post
92+
): Boolean {
93+
return oldItem == newItem
94+
}
95+
96+
override fun areContentsTheSame(
97+
oldItem: Post,
98+
newItem: Post
99+
): Boolean {
100+
return oldItem.id == newItem.id
101+
}
102+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package com.smarttoolfactory.postdynamichilt.postlist
2+
3+
import android.os.Bundle
4+
import android.view.View
5+
import androidx.core.os.bundleOf
6+
import androidx.fragment.app.viewModels
7+
import androidx.navigation.fragment.findNavController
8+
import androidx.recyclerview.widget.LinearLayoutManager
9+
import com.smarttoolfactory.core.ui.base.DynamicNavigationFragment
10+
import com.smarttoolfactory.postdynamichilt.R
11+
import com.smarttoolfactory.postdynamichilt.databinding.FragmentPostListBinding
12+
import dagger.hilt.android.AndroidEntryPoint
13+
14+
@AndroidEntryPoint
15+
class PostListFragment : DynamicNavigationFragment<FragmentPostListBinding>() {
16+
17+
override fun getLayoutRes(): Int = R.layout.fragment_post_list
18+
19+
private val viewModel: PostListViewModel by viewModels()
20+
21+
private lateinit var postListAdapter: PostListAdapter
22+
23+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
24+
super.onViewCreated(view, savedInstanceState)
25+
viewModel.getPosts()
26+
}
27+
28+
override fun bindViews() {
29+
30+
dataBinding.viewModel = viewModel
31+
32+
dataBinding.recyclerView.apply {
33+
34+
// Set Layout manager
35+
this.layoutManager =
36+
LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
37+
38+
postListAdapter = PostListAdapter(
39+
R.layout.row_post,
40+
viewModel::onClick
41+
)
42+
43+
// Set RecyclerViewAdapter
44+
this.adapter = postListAdapter
45+
}
46+
47+
val swipeRefreshLayout = dataBinding.swipeRefreshLayout
48+
49+
swipeRefreshLayout.setOnRefreshListener {
50+
swipeRefreshLayout.isRefreshing = false
51+
viewModel.refreshPosts()
52+
}
53+
54+
subscribeGoToDetailScreen()
55+
}
56+
57+
private fun subscribeGoToDetailScreen() {
58+
59+
viewModel.goToDetailScreen.observe(
60+
viewLifecycleOwner,
61+
{
62+
63+
it.getContentIfNotHandled()?.let { post ->
64+
val bundle = bundleOf("post" to post)
65+
66+
findNavController().navigate(
67+
R.id.nav_graph_post_detail,
68+
bundle
69+
)
70+
}
71+
}
72+
)
73+
}
74+
}
8.66 KB
Loading[フレーム]
9.42 KB
Loading[フレーム]
9.32 KB
Loading[フレーム]
9.52 KB
Loading[フレーム]

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /