Skip to content

Instantly share code, notes, and snippets.

@folkyatina
Last active December 6, 2018 12:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save folkyatina/12ba6d1e9763cec7bb803264a2e7d423 to your computer and use it in GitHub Desktop.
Save folkyatina/12ba6d1e9763cec7bb803264a2e7d423 to your computer and use it in GitHub Desktop.
Super simple MVP for Android + Navigator as an example
package com.songsterr.mvp
import android.arch.lifecycle.LifecycleOwner
import android.arch.lifecycle.ViewModel
import android.support.v4.app.Fragment
import org.koin.android.viewmodel.ext.android.sharedViewModel
import org.koin.android.viewmodel.ext.android.viewModel
// Immutable view state
interface ViewState
interface View<in S : ViewState> {
fun render(state: S)
}
abstract class Presenter<S : ViewState>(initialState: S) : ViewModel() {
protected var view: View<S>? = null
protected var state: S = initialState
set(value) {
field = value
view?.render(state)
}
fun takeView(view: View<S>) {
this.view = view
view.render(state)
}
fun dropView() {
this.view = null
}
}
inline fun <reified T : ViewModel> LifecycleOwner.presenter(): Lazy<T> {
return this.viewModel()
}
inline fun <reified T : ViewModel> Fragment.sharedPresenter(): Lazy<T> {
return this.sharedViewModel()
}
package com.songsterr.mvp
/**
* Here is a simple navigational module that can only safely start activities for result
*/
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentActivity
private const val RC_NAVIGATOR = 0x99
private const val FRAGMENT_TAG = "navigation"
typealias Success = (Int, Intent?) -> Unit
typealias Failure = (ActivityNotFoundException) -> Unit
object Navigation {
@JvmStatic
fun init(activity: FragmentActivity) {
val manager = activity.supportFragmentManager
if (manager.findFragmentByTag(FRAGMENT_TAG) == null) {
manager
.beginTransaction()
.add(NavigatorFragment(), FRAGMENT_TAG)
.commit()
}
}
}
data class NavigatorState(val intent: Intent? = null,
val success: Success? = null,
val failure: Failure? = null) : ViewState {
fun isOccupied(): Boolean {
return intent != null || success != null
}
}
class Navigator : Presenter<NavigatorState>(NavigatorState()) {
/**
* @param intent intent to start Activity with
* @param success success callback, if not null it will use startActivityForResult() and wait for response
* @param failure failure callback, if not null will be invoked in case of ActivityNotFoundException
*
* @return true if Activity is set for launching, false if Navigator is occupied
*/
fun launchActivity(intent: Intent, success: Success? = null, failure: Failure? = null): Boolean {
if (state.isOccupied()) {
return false
}
state = NavigatorState(intent, success, failure)
return true
}
fun openUrl(url: String, failure: Failure?): Boolean = launchActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)), failure = failure)
fun navigationLaunched() {
state = NavigatorState(null, success = state.success, failure = state.failure)
}
fun navigationResult(resultCode: Int, data: Intent?) {
state.success?.invoke(resultCode, data)
state = NavigatorState()
}
fun navigationFailed(e: ActivityNotFoundException) {
state.failure?.invoke(e)
state = NavigatorState()
}
}
class NavigatorFragment : Fragment(), View<NavigatorState> {
private val presenter: Navigator by sharedPresenter()
override fun onAttach(context: Context?) {
super.onAttach(context)
presenter.takeView(this)
}
override fun onDestroy() {
super.onDestroy()
presenter.dropView()
}
override fun render(state: NavigatorState) {
if (state.intent != null) {
try {
if (state.success != null) {
startActivityForResult(state.intent, RC_NAVIGATOR)
} else {
startActivity(state.intent)
}
presenter.navigationLaunched()
} catch (e: ActivityNotFoundException) {
presenter.navigationFailed(e)
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == RC_NAVIGATOR) {
presenter.navigationResult(resultCode, data)
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment