diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c8fbed8..b2d252b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,13 +3,32 @@
package="dev.carlos.acronyms"
>
+
+
+ android:hardwareAccelerated="true"
+ >
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/dev/carlos/acronyms/MainApplication.kt b/app/src/main/java/dev/carlos/acronyms/MainApplication.kt
new file mode 100644
index 0000000..ea7d95c
--- /dev/null
+++ b/app/src/main/java/dev/carlos/acronyms/MainApplication.kt
@@ -0,0 +1,32 @@
+package dev.carlos.acronyms
+
+import android.app.Application
+import dev.carlos.acronyms.di.appModules
+import org.koin.android.ext.koin.androidContext
+import org.koin.core.context.startKoin
+import timber.log.Timber
+
+@Suppress("unused")
+class MainApplication : Application() {
+ override fun onCreate() {
+ super.onCreate()
+ setupLogger()
+ setupDi()
+ }
+
+ private fun setupLogger() {
+ Timber.plant(Timber.DebugTree())
+ }
+
+ private fun setupDi() {
+ startKoin {
+ androidContext(this@MainApplication)
+
+ modules(
+ listOf(
+ appModules
+ )
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/dev/carlos/acronyms/di/AppModules.kt b/app/src/main/java/dev/carlos/acronyms/di/AppModules.kt
new file mode 100644
index 0000000..30e1cff
--- /dev/null
+++ b/app/src/main/java/dev/carlos/acronyms/di/AppModules.kt
@@ -0,0 +1,17 @@
+package dev.carlos.acronyms.di
+
+import dev.carlos.acronyms.BuildConfig
+import dev.carlos.acronyms.viewmodel.NavigationViewmodel
+import dev.carlos.core.domain.network.RemoteClient
+import dev.carlos.core.navigation.NavigationRouter
+import org.koin.androidx.viewmodel.dsl.viewModel
+import org.koin.dsl.module
+
+val appModules = module {
+ // Retrofit
+ single { RemoteClient(BuildConfig.BASE_ENDPOINT) }
+
+ // Navigation
+ single { NavigationRouter() }
+ viewModel { NavigationViewmodel() }
+}
diff --git a/app/src/main/java/dev/carlos/acronyms/viewmodel/NavigationViewmodel.kt b/app/src/main/java/dev/carlos/acronyms/viewmodel/NavigationViewmodel.kt
new file mode 100644
index 0000000..0329917
--- /dev/null
+++ b/app/src/main/java/dev/carlos/acronyms/viewmodel/NavigationViewmodel.kt
@@ -0,0 +1,18 @@
+package dev.carlos.acronyms.viewmodel
+
+import android.os.Bundle
+import androidx.annotation.IdRes
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import dev.carlos.core.navigation.NavigationEvent
+
+class NavigationViewmodel : ViewModel() {
+
+ val navigate = MutableLiveData>()
+
+ fun onNavigationEvent(event: NavigationEvent) {
+ when (event) {
+ else -> throw NoWhenBranchMatchedException("Undefined navigation event parent")
+ }
+ }
+}
diff --git a/app/src/main/java/dev/carlos/acronyms/views/MainActivity.kt b/app/src/main/java/dev/carlos/acronyms/views/MainActivity.kt
new file mode 100644
index 0000000..8d5b26b
--- /dev/null
+++ b/app/src/main/java/dev/carlos/acronyms/views/MainActivity.kt
@@ -0,0 +1,43 @@
+package dev.carlos.acronyms.views
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.navigation.findNavController
+import androidx.navigation.ui.NavigationUI
+import dev.carlos.acronyms.R
+import dev.carlos.acronyms.di.appModules
+import dev.carlos.acronyms.viewmodel.NavigationViewmodel
+import dev.carlos.core.extensions.findNavHostFragment
+import dev.carlos.core.extensions.observeNonNull
+import dev.carlos.core.navigation.NavigationRouter
+import org.koin.android.ext.android.inject
+import org.koin.android.ext.koin.androidContext
+import org.koin.androidx.viewmodel.ext.android.viewModel
+import org.koin.core.context.startKoin
+
+class MainActivity : AppCompatActivity() {
+
+ private val navController by lazy { findNavController(R.id.main_fragment_container) }
+ private val navRouter: NavigationRouter by inject()
+ private val viewModel: NavigationViewmodel by viewModel()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ setupNavigation()
+ }
+
+ private fun setupNavigation() {
+ navController.setGraph(R.navigation.main_navigation)
+ navRouter.setOnNavigationEvent {
+ viewModel.onNavigationEvent(it)
+ }
+ viewModel.navigate.observeNonNull(this) { pair ->
+ // Pair.first is a Navigation Id
+ // Pair.second is a Bundle
+ navController.navigate(pair.first, pair.second)
+ }
+
+ NavigationUI.setupActionBarWithNavController(this, navController)
+ }
+}
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..a21b3e1
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/navigation/main_navigation.xml b/app/src/main/res/navigation/main_navigation.xml
new file mode 100644
index 0000000..f17e2a2
--- /dev/null
+++ b/app/src/main/res/navigation/main_navigation.xml
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/core/src/main/java/dev/carlos/core/domain/Either.kt b/core/src/main/java/dev/carlos/core/domain/Either.kt
new file mode 100644
index 0000000..f51ce75
--- /dev/null
+++ b/core/src/main/java/dev/carlos/core/domain/Either.kt
@@ -0,0 +1,33 @@
+package dev.carlos.core.domain
+
+sealed class Either {
+
+ data class Left(val a: L) : Either()
+ data class Right(val b: R) : Either()
+
+ val isRight get() = this is Right
+ val isLeft get() = this is Left
+
+ fun left(a: L) = Left(a)
+ fun right(b: R) = Right(b)
+
+ fun either(fnL: (L) -> Any, fnR: (R) -> Any): Any =
+ when (this) {
+ is Left -> fnL(a)
+ is Right -> fnR(b)
+ }
+}
+
+fun ((A) -> B).c(f: (B) -> C): (A) -> C = {
+ f(this(it))
+}
+
+fun Either.flatMap(fn: (R) -> Either): Either =
+ when (this) {
+ is Either.Left -> Either.Left(
+ a
+ )
+ is Either.Right -> fn(b)
+ }
+
+fun Either.map(fn: (R) -> (T)): Either = this.flatMap(fn.c(::right))
diff --git a/core/src/main/java/dev/carlos/core/domain/network/RemoteClient.kt b/core/src/main/java/dev/carlos/core/domain/network/RemoteClient.kt
new file mode 100644
index 0000000..ae41e79
--- /dev/null
+++ b/core/src/main/java/dev/carlos/core/domain/network/RemoteClient.kt
@@ -0,0 +1,36 @@
+package dev.carlos.core.domain.network
+
+import dev.carlos.core.BuildConfig
+import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import retrofit2.Retrofit
+import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
+import retrofit2.converter.gson.GsonConverterFactory
+import timber.log.Timber
+import java.util.concurrent.TimeUnit
+
+private const val TIMEOUT_IN_SECONDS = 60L
+
+class RemoteClient(endpoint: String) {
+ private val logger = HttpLoggingInterceptor { message -> Timber.d(message) }.setLevel(getLoggerLevel())
+
+ private val client = OkHttpClient.Builder()
+ .addInterceptor(logger)
+ .readTimeout(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS)
+ .connectTimeout(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS)
+ .build()
+
+ private val retrofit: Retrofit = Retrofit.Builder()
+ .baseUrl(endpoint)
+ .addConverterFactory(GsonConverterFactory.create())
+ .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
+ .client(client)
+ .build()
+
+ fun getClient(api: Class): T = retrofit.create(api)
+
+ private fun getLoggerLevel() = when (BuildConfig.DEBUG) {
+ true -> HttpLoggingInterceptor.Level.BASIC
+ false -> HttpLoggingInterceptor.Level.NONE
+ }
+}
\ No newline at end of file
diff --git a/core/src/main/java/dev/carlos/core/domain/network/RequestStatus.kt b/core/src/main/java/dev/carlos/core/domain/network/RequestStatus.kt
new file mode 100644
index 0000000..1c8cafc
--- /dev/null
+++ b/core/src/main/java/dev/carlos/core/domain/network/RequestStatus.kt
@@ -0,0 +1,8 @@
+package dev.carlos.core.domain.network
+
+sealed class RequestStatus {
+ object Ready : RequestStatus()
+ object Loading : RequestStatus()
+ object Errored : RequestStatus()
+ object Empty : RequestStatus()
+}
diff --git a/core/src/main/java/dev/carlos/core/extensions/ApplicationExtension.kt b/core/src/main/java/dev/carlos/core/extensions/ApplicationExtension.kt
new file mode 100644
index 0000000..be1050e
--- /dev/null
+++ b/core/src/main/java/dev/carlos/core/extensions/ApplicationExtension.kt
@@ -0,0 +1,8 @@
+package dev.carlos.core.extensions
+
+import android.content.Context
+import android.widget.Toast
+
+fun Context.toast(message: String) {
+ Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
+}
diff --git a/core/src/main/java/dev/carlos/core/extensions/DataExtensions.kt b/core/src/main/java/dev/carlos/core/extensions/DataExtensions.kt
new file mode 100644
index 0000000..1176e23
--- /dev/null
+++ b/core/src/main/java/dev/carlos/core/extensions/DataExtensions.kt
@@ -0,0 +1,15 @@
+package dev.carlos.core.extensions
+
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.Observer
+
+fun LiveData.observeNonNull(owner: LifecycleOwner, func: (T) -> Unit) {
+ observe(owner, Observer {
+ it?.let {
+ func(it)
+ }
+ })
+}
+
+fun Int.wasUpdated() = this > 0
diff --git a/core/src/main/java/dev/carlos/core/extensions/NavigationExtensions.kt b/core/src/main/java/dev/carlos/core/extensions/NavigationExtensions.kt
new file mode 100644
index 0000000..97025f0
--- /dev/null
+++ b/core/src/main/java/dev/carlos/core/extensions/NavigationExtensions.kt
@@ -0,0 +1,32 @@
+package dev.carlos.core.extensions
+
+import android.os.Bundle
+import android.view.View
+import androidx.annotation.IdRes
+import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentManager
+import androidx.navigation.fragment.NavHostFragment
+
+fun AppCompatActivity.findNavHostFragment(@IdRes id: Int) =
+ supportFragmentManager.findFragmentById(id) as NavHostFragment
+
+fun NavHostFragment.registerOnFragmentViewCreated(
+ recursive: Boolean = true,
+ listener: (currentFragment: Fragment) -> Unit
+) {
+ childFragmentManager
+ .registerFragmentLifecycleCallbacks(object : FragmentManager.FragmentLifecycleCallbacks() {
+ override fun onFragmentViewCreated(
+ fm: FragmentManager,
+ f: Fragment,
+ v: View,
+ savedInstanceState: Bundle?
+ ) {
+ super.onFragmentViewCreated(fm, f, v, savedInstanceState)
+ listener(f)
+ }
+ }, recursive)
+}
+
+fun buildNavigation(@IdRes id: Int, bundle: Bundle = Bundle()) = Pair(id, bundle)
diff --git a/core/src/main/java/dev/carlos/core/extensions/ViewExtensions.kt b/core/src/main/java/dev/carlos/core/extensions/ViewExtensions.kt
new file mode 100644
index 0000000..3241e1f
--- /dev/null
+++ b/core/src/main/java/dev/carlos/core/extensions/ViewExtensions.kt
@@ -0,0 +1,60 @@
+package dev.carlos.core.extensions
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.LayoutRes
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.RecyclerView
+
+fun View.enable() {
+ isEnabled = true
+}
+
+fun View.disable() {
+ isEnabled = false
+}
+
+fun View.visible() {
+ visibility = View.VISIBLE
+}
+
+fun View.invisible() {
+ visibility = View.INVISIBLE
+}
+
+fun View.transparent() {
+ alpha = 0f
+}
+
+fun View.opaque() {
+ alpha = 1f
+}
+
+fun View.gone() {
+ visibility = View.GONE
+}
+
+fun ViewGroup.inflate(@LayoutRes layout: Int, attachToRoot: Boolean = false): View =
+ LayoutInflater
+ .from(context)
+ .inflate(layout, this, attachToRoot)
+
+fun RecyclerView.Adapter<*>.autoNotify(oldList: List, newList: List, compare: (T, T) -> Boolean) {
+ val diff = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
+
+ override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
+ return compare(oldList[oldItemPosition], newList[newItemPosition])
+ }
+
+ override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
+ return oldList[oldItemPosition] == newList[newItemPosition]
+ }
+
+ override fun getOldListSize() = oldList.size
+
+ override fun getNewListSize() = newList.size
+ })
+
+ diff.dispatchUpdatesTo(this)
+}
diff --git a/core/src/main/java/dev/carlos/core/navigation/NavigationEvent.kt b/core/src/main/java/dev/carlos/core/navigation/NavigationEvent.kt
new file mode 100644
index 0000000..d6f0f83
--- /dev/null
+++ b/core/src/main/java/dev/carlos/core/navigation/NavigationEvent.kt
@@ -0,0 +1,7 @@
+package dev.carlos.core.navigation
+
+interface NavigationEvent
+
+interface NavigationController {
+ fun sendNavigation(event: NavigationEvent)
+}
diff --git a/core/src/main/java/dev/carlos/core/navigation/NavigationRouter.kt b/core/src/main/java/dev/carlos/core/navigation/NavigationRouter.kt
new file mode 100644
index 0000000..638a009
--- /dev/null
+++ b/core/src/main/java/dev/carlos/core/navigation/NavigationRouter.kt
@@ -0,0 +1,13 @@
+package dev.carlos.core.navigation
+
+class NavigationRouter : NavigationController {
+ private var onNavigationEvent: (NavigationEvent) -> Unit = {}
+
+ override fun sendNavigation(event: NavigationEvent) {
+ onNavigationEvent(event)
+ }
+
+ fun setOnNavigationEvent(listener: (NavigationEvent) -> Unit) {
+ onNavigationEvent = listener
+ }
+}