Skip to content

Commit 0bf69aa

Browse files
authored
Adding LiveData and other useful extensions. (#101)
1 parent 5c94c78 commit 0bf69aa

18 files changed

+178
-73
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.rocketinsights.android.extensions
2+
3+
import android.app.Activity
4+
import android.view.View
5+
import androidx.core.view.ViewCompat
6+
import androidx.core.view.WindowInsetsCompat
7+
import com.rocketinsights.android.ui.components.DialogBuilder
8+
9+
fun Activity.hideKeyboard(target: View? = null) {
10+
val focus = target ?: currentFocus
11+
focus?.let { view ->
12+
val insetsController = ViewCompat.getWindowInsetsController(view)
13+
insetsController?.hide(WindowInsetsCompat.Type.ime())
14+
}
15+
}
16+
17+
fun Activity.showKeyboard(target: View? = null) {
18+
val focus = target ?: currentFocus
19+
focus?.let { view ->
20+
val insetsController = ViewCompat.getWindowInsetsController(view)
21+
insetsController?.show(WindowInsetsCompat.Type.ime())
22+
}
23+
}
24+
25+
fun Activity.showDialog(setup: DialogBuilder.() -> Unit) =
26+
DialogBuilder(this, setup = setup).build().show()

app/src/main/java/com/rocketinsights/android/extensions/FragmentExt.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import androidx.core.view.WindowInsetsCompat
88
import androidx.core.view.WindowInsetsControllerCompat
99
import androidx.fragment.app.Fragment
1010
import androidx.navigation.findNavController
11+
import com.rocketinsights.android.ui.components.DialogBuilder
1112

1213
fun Fragment.setupActionBar(toolbar: Toolbar) {
1314
val activity = requireActivity() as AppCompatActivity
@@ -17,16 +18,23 @@ fun Fragment.setupActionBar(toolbar: Toolbar) {
1718
}
1819
}
1920

20-
fun Fragment.showSystemUI(root: View) {
21+
fun Fragment.showDialog(setup: DialogBuilder.() -> Unit) =
22+
DialogBuilder(requireContext(), setup = setup).build().show()
23+
24+
fun Fragment.showNavBar(root: View) {
2125
WindowCompat.setDecorFitsSystemWindows(requireActivity().window, true)
2226
WindowInsetsControllerCompat(requireActivity().window, root).run {
2327
show(WindowInsetsCompat.Type.navigationBars())
2428
}
2529
}
2630

27-
fun Fragment.hideSystemUI(root: View) {
31+
fun Fragment.hideNavBar(root: View) {
2832
WindowCompat.setDecorFitsSystemWindows(requireActivity().window, false)
2933
WindowInsetsControllerCompat(requireActivity().window, root).run {
3034
hide(WindowInsetsCompat.Type.navigationBars())
3135
}
3236
}
37+
38+
fun Fragment.hideKeyboard(target: View? = null) = requireActivity().hideKeyboard(target)
39+
40+
fun Fragment.showKeyboard(target: View? = null) = requireActivity().showKeyboard(target)

app/src/main/java/com/rocketinsights/android/extensions/LayoutProgressExt.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ package com.rocketinsights.android.extensions
22

33
import com.rocketinsights.android.databinding.LayoutProgressBinding
44

5+
private const val FADE_DURATION = 500L
6+
57
fun LayoutProgressBinding.show() {
6-
root.show()
8+
root.fadeIn(duration = FADE_DURATION)
79
}
810

911
fun LayoutProgressBinding.hide() {
10-
root.hide()
12+
root.fadeOut(duration = FADE_DURATION)
1113
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.rocketinsights.android.extensions
2+
3+
import androidx.annotation.MainThread
4+
import androidx.lifecycle.LifecycleOwner
5+
import androidx.lifecycle.LiveData
6+
import androidx.lifecycle.MutableLiveData
7+
import androidx.lifecycle.Observer
8+
import com.rocketinsights.android.viewmodels.event.Event
9+
import com.rocketinsights.android.viewmodels.event.EventObserver
10+
11+
fun <T> MutableLiveData<Event<T>>.postEvent(event: T) {
12+
postValue(Event(event))
13+
}
14+
15+
fun <T> MutableLiveData<Event<T>>.setEvent(event: T) {
16+
value = Event(event)
17+
}
18+
19+
@MainThread
20+
inline fun <T> LiveData<Event<T>>.observeEvent(
21+
owner: LifecycleOwner,
22+
crossinline onChanged: (T) -> Unit
23+
): Observer<Event<T>> {
24+
val wrappedObserver = EventObserver<T> { value ->
25+
onChanged.invoke(value)
26+
}
27+
observe(owner, wrappedObserver)
28+
return wrappedObserver
29+
}

app/src/main/java/com/rocketinsights/android/extensions/ViewExt.kt

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,48 @@ import android.view.View
55
import android.view.View.GONE
66
import android.view.View.INVISIBLE
77
import android.view.View.VISIBLE
8+
import android.view.ViewTreeObserver
89

9-
private const val FADE_IN_DURATION = 300L
10-
private const val FADE_OUT_DURATION = 300L
10+
private const val FADE_IN_DURATION = 500L
11+
private const val FADE_OUT_DURATION = 500L
1112

1213
fun View.show() {
1314
if (visibility == VISIBLE) return
1415
visibility = VISIBLE
1516
}
1617

1718
fun View.hide() {
19+
if (visibility == GONE) return
20+
visibility = GONE
21+
}
22+
23+
fun View.invisible() {
1824
if (visibility == INVISIBLE) return
1925
visibility = INVISIBLE
2026
}
2127

22-
fun View.remove() {
23-
if (visibility == GONE) return
24-
visibility = GONE
28+
fun View.show(show: Boolean) {
29+
if (show) {
30+
show()
31+
} else {
32+
hide()
33+
}
34+
}
35+
36+
fun View.invisible(invisible: Boolean) {
37+
if (invisible) {
38+
invisible()
39+
} else {
40+
show()
41+
}
2542
}
2643

44+
fun View.isVisible() = visibility == VISIBLE
45+
46+
fun View.isGone() = visibility == GONE
47+
48+
fun View.isInvisible() = visibility == INVISIBLE
49+
2750
fun View.enable() {
2851
if (isEnabled) return
2952
isEnabled = true
@@ -34,10 +57,17 @@ fun View.disable() {
3457
isEnabled = false
3558
}
3659

60+
fun View.enable(enable: Boolean) {
61+
if (enable) enable()
62+
else disable()
63+
}
64+
3765
fun View.fadeIn(
3866
duration: Long = FADE_IN_DURATION,
3967
listener: Animator.AnimatorListener? = null
4068
) {
69+
if (isVisible()) return
70+
alpha = 0F
4171
show()
4272
animate()
4373
.alpha(1F)
@@ -49,9 +79,21 @@ fun View.fadeOut(
4979
duration: Long = FADE_OUT_DURATION,
5080
listener: Animator.AnimatorListener? = null
5181
) {
82+
if (!isVisible()) return
5283
animate()
5384
.alpha(0F)
5485
.setDuration(duration)
5586
.setListener(listener)
5687
.withEndAction { hide() }
5788
}
89+
90+
inline fun View.afterMeasured(crossinline action: View.() -> Unit) {
91+
viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
92+
override fun onGlobalLayout() {
93+
viewTreeObserver.removeOnGlobalLayoutListener(this)
94+
if (measuredWidth > 0 && measuredHeight > 0) {
95+
action()
96+
}
97+
}
98+
})
99+
}

app/src/main/java/com/rocketinsights/android/ui/CalendarFragment.kt

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import androidx.recyclerview.widget.RecyclerView
1010
import com.rocketinsights.android.R
1111
import com.rocketinsights.android.databinding.FragmentCalendarBinding
1212
import com.rocketinsights.android.extensions.hide
13+
import com.rocketinsights.android.extensions.observeEvent
1314
import com.rocketinsights.android.extensions.setupActionBar
1415
import com.rocketinsights.android.extensions.show
1516
import com.rocketinsights.android.extensions.showToast
@@ -73,33 +74,29 @@ class CalendarFragment : Fragment(R.layout.fragment_calendar) {
7374
eventsAdapter.submitList(events)
7475
}
7576

76-
viewModel.calendarState.observe(viewLifecycleOwner) {
77-
it.getContentIfNotHandled()?.let { calendarState ->
78-
when (calendarState) {
79-
CalendarState.Loading -> binding.progress.show()
80-
CalendarState.Success -> {
81-
binding.progress.hide()
82-
}
83-
is CalendarState.Error -> {
84-
binding.progress.hide()
85-
requireContext().showToast(
86-
getString(R.string.calendar_error)
87-
)
88-
}
77+
viewModel.calendarState.observeEvent(viewLifecycleOwner) { calendarState ->
78+
when (calendarState) {
79+
CalendarState.Loading -> binding.progress.show()
80+
CalendarState.Success -> {
81+
binding.progress.hide()
8982
}
90-
}
91-
}
92-
93-
permissionsViewModel.permissionsResult.observe(viewLifecycleOwner) {
94-
it.getContentIfNotHandled()?.let { permissionResult ->
95-
if (permissionResult is PermissionsResult.PermissionsError) {
83+
is CalendarState.Error -> {
84+
binding.progress.hide()
9685
requireContext().showToast(
97-
getString(R.string.calendar_permission_not_granted)
86+
getString(R.string.calendar_error)
9887
)
99-
findNavController().popBackStack()
10088
}
10189
}
10290
}
91+
92+
permissionsViewModel.permissionsResult.observeEvent(viewLifecycleOwner) { permissionResult ->
93+
if (permissionResult is PermissionsResult.PermissionsError) {
94+
requireContext().showToast(
95+
getString(R.string.calendar_permission_not_granted)
96+
)
97+
findNavController().popBackStack()
98+
}
99+
}
103100
}
104101

105102
private val listDivider: DividerItemDecoration by lazy {
@@ -108,7 +105,8 @@ class CalendarFragment : Fragment(R.layout.fragment_calendar) {
108105
RecyclerView.VERTICAL
109106
)
110107

111-
val separatorDrawable = ContextCompat.getDrawable(requireContext(), R.drawable.list_item_separator_default)
108+
val separatorDrawable =
109+
ContextCompat.getDrawable(requireContext(), R.drawable.list_item_separator_default)
112110
dividerItemDecoration.setDrawable(separatorDrawable!!)
113111

114112
dividerItemDecoration

app/src/main/java/com/rocketinsights/android/ui/ContainerTransformFragment.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import com.rocketinsights.android.databinding.FragmentContainerTransformBinding
1212
import com.rocketinsights.android.extensions.fadeIn
1313
import com.rocketinsights.android.extensions.fadeOut
1414
import com.rocketinsights.android.extensions.hide
15-
import com.rocketinsights.android.extensions.remove
15+
import com.rocketinsights.android.extensions.invisible
1616
import com.rocketinsights.android.extensions.setupActionBar
1717
import com.rocketinsights.android.extensions.show
1818
import com.rocketinsights.android.extensions.viewBinding
@@ -73,13 +73,13 @@ class ContainerTransformFragment : Fragment(R.layout.fragment_container_transfor
7373
binding.run {
7474
textTransformButtonInfo.fadeOut()
7575
cardTransformInfo.show()
76-
buttonTransformInfo.hide()
76+
buttonTransformInfo.invisible()
7777
}
7878
}
7979

8080
private fun collapseButton() {
8181
// remove auxiliary scrim
82-
binding.scrimInfoCard.remove()
82+
binding.scrimInfoCard.hide()
8383
// create and start collapse transition from card to button
8484
val transform = MaterialContainerTransform().apply {
8585
startView = binding.cardTransformInfo
@@ -90,7 +90,7 @@ class ContainerTransformFragment : Fragment(R.layout.fragment_container_transfor
9090
TransitionManager.beginDelayedTransition(binding.root, transform)
9191
binding.run {
9292
buttonTransformInfo.show()
93-
cardTransformInfo.hide()
93+
cardTransformInfo.invisible()
9494
textTransformButtonInfo.fadeIn()
9595
}
9696
}

app/src/main/java/com/rocketinsights/android/ui/MainFragment.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ import com.rocketinsights.android.extensions.getIOErrorMessage
2323
import com.rocketinsights.android.extensions.getUriForFile
2424
import com.rocketinsights.android.extensions.setupActionBar
2525
import com.rocketinsights.android.extensions.show
26+
import com.rocketinsights.android.extensions.showDialog
2627
import com.rocketinsights.android.extensions.showToast
2728
import com.rocketinsights.android.extensions.viewBinding
28-
import com.rocketinsights.android.ui.components.dialog
2929
import com.rocketinsights.android.viewmodels.MainMessageState
3030
import com.rocketinsights.android.viewmodels.MainViewModel
3131
import com.rocketinsights.android.viewmodels.PhotoViewModel
@@ -127,7 +127,7 @@ class MainFragment : Fragment(R.layout.fragment_main) {
127127
}
128128

129129
private fun showDslDialog() {
130-
dialog {
130+
showDialog {
131131
titleRes = R.string.dialog_title
132132
contentRes = R.string.dialog_content
133133
cancelable = false

app/src/main/java/com/rocketinsights/android/ui/MessagesFragment.kt

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import com.google.android.material.transition.MaterialFadeThrough
99
import com.rocketinsights.android.R
1010
import com.rocketinsights.android.databinding.FragmentMessagesBinding
1111
import com.rocketinsights.android.extensions.getIOErrorMessage
12+
import com.rocketinsights.android.extensions.observeEvent
1213
import com.rocketinsights.android.extensions.setupActionBar
1314
import com.rocketinsights.android.extensions.showToast
1415
import com.rocketinsights.android.extensions.viewBinding
@@ -82,13 +83,11 @@ class MessagesFragment : Fragment(R.layout.fragment_messages) {
8283
}
8384

8485
// observe connectivity status
85-
connectivityViewModel.status.observe(viewLifecycleOwner) {
86-
it.getContentIfNotHandled()?.let { status ->
87-
if (status == InternetManager.ConnectivityStatus.CONNECTED) {
88-
viewModel.refreshMessages()
89-
} else {
90-
requireContext().showToast(getString(R.string.connectivity_offline))
91-
}
86+
connectivityViewModel.status.observeEvent(viewLifecycleOwner) { status ->
87+
if (status == InternetManager.ConnectivityStatus.CONNECTED) {
88+
viewModel.refreshMessages()
89+
} else {
90+
requireContext().showToast(getString(R.string.connectivity_offline))
9291
}
9392
}
9493
}

app/src/main/java/com/rocketinsights/android/ui/SplashFragment.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import androidx.navigation.fragment.findNavController
88
import com.google.android.material.transition.MaterialFadeThrough
99
import com.rocketinsights.android.R
1010
import com.rocketinsights.android.databinding.FragmentSplashBinding
11-
import com.rocketinsights.android.extensions.hideSystemUI
12-
import com.rocketinsights.android.extensions.showSystemUI
11+
import com.rocketinsights.android.extensions.hideNavBar
12+
import com.rocketinsights.android.extensions.showNavBar
1313
import com.rocketinsights.android.extensions.viewBinding
1414
import com.rocketinsights.android.viewmodels.UserViewModel
1515
import kotlinx.coroutines.FlowPreview
@@ -36,12 +36,12 @@ class SplashFragment : Fragment(R.layout.fragment_splash) {
3636

3737
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
3838
super.onViewCreated(view, savedInstanceState)
39-
hideSystemUI(binding.root)
39+
hideNavBar(binding.root)
4040
setupObservers()
4141
}
4242

4343
override fun onDestroyView() {
44-
showSystemUI(binding.root)
44+
showNavBar(binding.root)
4545
super.onDestroyView()
4646
}
4747

app/src/main/java/com/rocketinsights/android/ui/components/DialogDsl.kt

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
package com.rocketinsights.android.ui.components
22

3-
import android.app.Activity
43
import android.content.Context
54
import android.view.View
65
import androidx.annotation.StringRes
76
import androidx.appcompat.app.AlertDialog
8-
import androidx.fragment.app.Fragment
97
import com.google.android.material.dialog.MaterialAlertDialogBuilder
108

119
/**
@@ -98,9 +96,3 @@ private fun <T, U> Any.quartus(
9896
}
9997
}
10098
}
101-
102-
fun Fragment.dialog(setup: DialogBuilder.() -> Unit) =
103-
DialogBuilder(requireContext(), setup = setup).build().show()
104-
105-
fun Activity.dialog(setup: DialogBuilder.() -> Unit) =
106-
DialogBuilder(this, setup = setup).build().show()

0 commit comments

Comments
 (0)