# Core Module
This is our ever changing collection of tools, helpers and base structures. It provides guidance
on how to use modern android, handle communication between viewModel and views, bind views and
much more.
## Views (a.k.a. where to start)
We, of course, provide base views and viewModels for your (and our) convenience. We currently
support `Activity`, `Fragment` and `DialogFragment`. They all come pre-equipped with:
- support for **system window insets**
- unidirectional communication between viewModel and view
- support for snackbars
- support for navController
### Example
Example covers basic implementation of views with [navigation](https://developer.android.com/guide/navigation/navigation-getting-started)
library enabled.
```kotlin
class ExampleViewModel : TeanityViewModel() {
fun navigateToHome() = // supports navigation from within viewModels
ExampleFragmentDirections.navigateHome().publish()
suspend fun hasCameraPermission() = // supports awaiting for activity contracts
ActivityResultContracts.RequestPermission().await(Manifest.permission.CAMERA)
}
```
```kotlin
class ExampleActivity : TeanityActivity() {
override val layoutRes = R.layout.activity_example
override val viewModel by lifecycleScope.viewModel() // uses Koin DI
override val navHostId = R.id.example_nav_host
}
```
```kotlin
class ExampleFragment : TeanityFragment() {
override val layoutRes = R.layout.fragment_example
override val viewModel by sharedViewModel() // uses Koin DI
}
```
## ViewModels
ViewModels are essential part of your app at this point. With that there's probably not a single
viewModel type that would fit all of your needs, so we provide 3 essential ones.
### Example
`TeanityViewModel` contains all the necessary extensions of normal viewModels you'll love. Starting
from ordered refreshes to communication to your view. It allows you to observe live data until is
the viewModel cleared and to create observable properties with native delegation!
```kotlin
class ExampleViewModel(
private val fooBar: FooBarUseCase
) : TeanityViewModel() {
@get:Bindable
var isFoo by observable(false, BR.foo)
private set
init {
fooBar.observe().observe {
// observe until the viewModel dies
}
}
fun navigateToHome() =
ExampleFragmentDirections.navigateHome().publish()
suspend fun hasCameraPermission() =
ActivityResultContracts.RequestPermission().await(Manifest.permission.CAMERA)
override suspend fun refresh() {
delay(3000) // long running task
}
}
```
`LoadingViewModel` allows you to launch loading tasks.
```kotlin
class ExampleLoadingViewModel : LoadingViewModel() {
init {
loading {
delay(3000)
}
}
}
```
`LiveStateViewModel` includes a stateful live data and so discourages you from using built-in
"observable" features of the base `TeanityViewModel`. All data handled within this viewModel should
be enclosed in sealed state you provide.
```kotlin
sealed class ExampleState {
object Loading : ExampleState()
data class LoadedStage1(
val title: String,
val subtitle: String,
val message: String
) : ExampleState()
data class LoadedStage2(
val list: List,
val diff: DiffUtil.DiffResult
) : ExampleState()
}
class ExampleLiveViewModel : LiveStateViewModel(ExampleState.Loading)
```
## Lists
Aren't recycler view adapters annoying? We're here to the rescue. No more annoying adapters for
RecyclerView. Create one that does everything for you, bind variables and focus on your UI.
### Example
```kotlin
class FooBar {
private val adapter = BindingAdapter(viewLifecycleOwner) {
it.setVariable(BR.viewModel, viewModel)
}
fun refreshList(newList: List) {
scope.launch(Dispatchers.Main) {
val callback = adapter.getCallbackFrom(newList)
val diff = withContext(Dispatchers.Default) {
DiffUtil.calculateDiff(callback)
}
adapter.update(newList, diff)
}
}
}
```
## Custom View Events
One will surely need to communicate to the view that custom event happened and ordering it to do
something. We thought of that and `ViewEvent`s with `*Executor`s are here to help. To execute them
automatically, please mind that your View needs to extend either of `Teanity*` Views, otherwise
you'd need to manage those events yourself. Once created an instance of the `ViewEvent` call
`.publish()` on them from your `TeanityViewModel`.
### Example
You can essentially define your `ViewEvent`s in two major ways. Inner or global events. Example
covers both of these use cases.
#### Inner
Event is strictly prohibited from being used by any other view than this one. Private methods can
be used like that, hence not exposing them and having inner event might be more desirable in some
cases.
```kotlin
class FooFragment : TeanityFragment() {
private fun moveView(which: Int) = TODO()
class AnimateViewEvent(
private val which: Int
) : ViewEvent(), FragmentExecutor {
override fun invoke(fragment: Fragment) {
if (fragment !is FooFragment) return
fragment.moveView(which)
}
}
}
```
#### Global
Event can be run from anywhere, anytime. Generally should not be picky about instance of the
fragment or activity.
```kotlin
class OpenUriEvent(
private val uri: Uri
) : ViewEvent(), ContextExecutor {
override fun invoke(context: Context) {
TODO("open uri")
}
}
```