Permalink
Browse files

Update MVP-Fragment sample to be closer to actual MVP pattern

  • Loading branch information...
Zhuinden committed Sep 2, 2018
1 parent 9972289 commit 725107ec9ac60848a1c4560a988a3b18a6b5fd66
Showing with 347 additions and 229 deletions.
  1. +29 −5 ...-fragments/src/main/java/com/zhuinden/simplestackdemoexamplefragments/application/MainActivity.kt
  2. +26 −15 ...huinden/simplestackdemoexamplefragments/presentation/paths/addoredittask/AddOrEditTaskFragment.kt
  3. +15 −6 ...com/zhuinden/simplestackdemoexamplefragments/presentation/paths/addoredittask/AddOrEditTaskKey.kt
  4. +19 −14 ...uinden/simplestackdemoexamplefragments/presentation/paths/addoredittask/AddOrEditTaskPresenter.kt
  5. +15 −4 .../com/zhuinden/simplestackdemoexamplefragments/presentation/paths/statistics/StatisticsFragment.kt
  6. +10 −1 .../java/com/zhuinden/simplestackdemoexamplefragments/presentation/paths/statistics/StatisticsKey.kt
  7. +3 −14 ...com/zhuinden/simplestackdemoexamplefragments/presentation/paths/statistics/StatisticsPresenter.kt
  8. +22 −22 .../com/zhuinden/simplestackdemoexamplefragments/presentation/paths/taskdetail/TaskDetailFragment.kt
  9. +10 −1 .../java/com/zhuinden/simplestackdemoexamplefragments/presentation/paths/taskdetail/TaskDetailKey.kt
  10. +20 −16 ...com/zhuinden/simplestackdemoexamplefragments/presentation/paths/taskdetail/TaskDetailPresenter.kt
  11. +4 −10 ...c/main/java/com/zhuinden/simplestackdemoexamplefragments/presentation/paths/tasks/TasksAdapter.kt
  12. +4 −4 ...ain/java/com/zhuinden/simplestackdemoexamplefragments/presentation/paths/tasks/TasksFilterType.kt
  13. +57 −57 .../main/java/com/zhuinden/simplestackdemoexamplefragments/presentation/paths/tasks/TasksFragment.kt
  14. +11 −2 ...s/src/main/java/com/zhuinden/simplestackdemoexamplefragments/presentation/paths/tasks/TasksKey.kt
  15. +55 −20 ...main/java/com/zhuinden/simplestackdemoexamplefragments/presentation/paths/tasks/TasksPresenter.kt
  16. +9 −20 ...ple-mvp-fragments/src/main/java/com/zhuinden/simplestackdemoexamplefragments/util/BaseFragment.kt
  17. +8 −10 ...le-mvp-fragments/src/main/java/com/zhuinden/simplestackdemoexamplefragments/util/BasePresenter.kt
  18. +7 −0 ...ple-mvp-fragments/src/main/java/com/zhuinden/simplestackdemoexamplefragments/util/MvpPresenter.kt
  19. +10 −0 ...ck-example-mvp-fragments/src/main/java/com/zhuinden/simplestackdemoexamplefragments/util/Utils.kt
  20. +0 −7 ...s/src/main/java/com/zhuinden/simplestackdemoexamplefragments/util/scopedservices/HasServices.java
  21. +8 −0 ...nts/src/main/java/com/zhuinden/simplestackdemoexamplefragments/util/scopedservices/HasServices.kt
  22. +5 −1 simple-stack/src/main/java/com/zhuinden/simplestack/ScopeManager.java
@@ -39,21 +39,39 @@ class MainActivity : AppCompatActivity(), StateChanger {
super.onCreate(savedInstanceState)
// this must be after `super.onCreate` otherwise multiple of them would exist after process death
supportFragmentManager.findFragmentByTag("MAIN_SCOPE_LISTENER").also { mainScopeListener ->
if (mainScopeListener == null) {
supportFragmentManager.beginTransaction().add(MainScopeListener(), "MAIN_SCOPE_LISTENER").commit()
supportFragmentManager.executePendingTransactions() // this guarantees that the retained fragment exists by the time the Repository needs it.
}
}
setContentView(R.layout.activity_main)
this.fragmentStateChanger = FragmentStateChanger(supportFragmentManager, R.id.root)
mainView.onCreate()
backstackDelegate.setStateChanger(this)
/*
* IMPORTANT!
*
* The order of lifecycle after process death is the following:
* - Activity onCreate
* - Activity.onStart
* - Fragment.onActivityCreated
* - Fragment.onCreateView
* - Fragment.onViewCreated
* - Activity.onPostCreate
*
* Therefore if ScopedServices is used with Fragments, the scopes must be built in onCreate,
* meaning the StateChanger must be set in onCreate, and onPostCreate runs too late,
* otherwise the scopes won't exist in onViewCreated after process death.
*/
}
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
backstackDelegate.setStateChanger(this)
mainView.onPostCreate()
}
@@ -76,6 +94,7 @@ class MainActivity : AppCompatActivity(), StateChanger {
override fun getSystemService(name: String): Any? = when {
TAG == name -> this
BACKSTACK_DELEGATE_TAG == name -> backstackDelegate
else -> super.getSystemService(name)
}
@@ -93,11 +112,16 @@ class MainActivity : AppCompatActivity(), StateChanger {
}
companion object {
const val TAG = "MainActivity"
private const val BACKSTACK_DELEGATE_TAG = "BackstackDelegate"
@SuppressLint("WrongConstant")
operator fun get(context: Context): MainActivity {
return context.getSystemService(TAG) as MainActivity
}
operator fun get(context: Context): MainActivity =
context.getSystemService(TAG) as MainActivity
@SuppressLint("WrongConstant")
fun getBackstackDelegate(context: Context): BackstackDelegate =
context.getSystemService(BACKSTACK_DELEGATE_TAG) as BackstackDelegate
const val TAG = "MainActivity"
}
}
@@ -4,8 +4,9 @@ import android.app.Activity
import android.os.Bundle
import android.view.View
import android.view.inputmethod.InputMethodManager
import com.zhuinden.simplestackdemoexamplefragments.application.Injector
import com.zhuinden.simplestackdemoexamplefragments.util.BaseFragment
import com.zhuinden.simplestackdemoexamplefragments.util.MvpPresenter
import com.zhuinden.simplestackdemoexamplefragments.util.backstackDelegate
import kotlinx.android.synthetic.main.path_addoredittask.*
import org.jetbrains.anko.sdk15.listeners.textChangedListener
@@ -14,10 +15,22 @@ import org.jetbrains.anko.sdk15.listeners.textChangedListener
*/
// UNSCOPED!
class AddOrEditTaskFragment : BaseFragment<AddOrEditTaskPresenter.ViewContract, AddOrEditTaskPresenter>(), AddOrEditTaskPresenter.ViewContract {
private val addOrEditTaskPresenter = Injector.get().addOrEditTaskPresenter()
class AddOrEditTaskFragment : BaseFragment<AddOrEditTaskFragment, AddOrEditTaskFragment.Presenter>() {
companion object {
const val CONTROLLER_TAG = "AddOrEditTaskPresenter"
}
interface Presenter: MvpPresenter<AddOrEditTaskFragment> {
fun onTitleChanged(title: String)
fun onDescriptionChanged(description: String)
override val presenter: AddOrEditTaskPresenter = addOrEditTaskPresenter
fun onSaveButtonClicked()
}
override val presenter: Presenter by lazy {
backstackDelegate.lookupService<AddOrEditTaskFragment.Presenter>(CONTROLLER_TAG)
}
override fun getThis(): AddOrEditTaskFragment = this
@@ -26,37 +39,35 @@ class AddOrEditTaskFragment : BaseFragment<AddOrEditTaskPresenter.ViewContract,
textAddTaskTitle.textChangedListener {
afterTextChanged { editable ->
addOrEditTaskPresenter.updateTitle(editable.toString())
presenter.onTitleChanged(editable.toString())
}
}
textAddTaskDescription.textChangedListener {
afterTextChanged { editable ->
addOrEditTaskPresenter.updateDescription(editable.toString())
presenter.onDescriptionChanged(editable.toString())
}
}
}
fun saveTask() = addOrEditTaskPresenter.saveTask()
fun navigateBack() {
addOrEditTaskPresenter.navigateBack()
}
override fun setTitle(title: String) {
fun setTitle(title: String) {
textAddTaskTitle.setText(title)
}
override fun setDescription(description: String) {
fun setDescription(description: String) {
textAddTaskDescription.setText(description)
}
override fun hideKeyboard() {
fun hideKeyboard() {
(requireActivity().getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager).also { imm ->
val view = view
if(view != null) {
imm.hideSoftInputFromWindow(view.windowToken, 0);
}
}
}
fun onSaveButtonClicked() {
presenter.onSaveButtonClicked()
}
}
@@ -2,22 +2,33 @@ package com.zhuinden.simplestackdemoexamplefragments.presentation.paths.addoredi
import android.support.v4.app.Fragment
import android.view.View
import com.zhuinden.simplestack.ScopedServices
import com.zhuinden.simplestackdemoexamplefragments.R
import com.zhuinden.simplestackdemoexamplefragments.application.BaseKey
import com.zhuinden.simplestackdemoexamplefragments.application.Injector
import com.zhuinden.simplestackdemoexamplefragments.application.Key
import com.zhuinden.simplestackdemoexamplefragments.util.scopedservices.HasServices
import kotlinx.android.parcel.Parcelize
/**
* Created by Zhuinden on 2018. 08. 20.
*/
sealed class AddOrEditTaskKey(val parent: Key, val taskId: String = "") : BaseKey() {
sealed class AddOrEditTaskKey(val parent: Key, val taskId: String = "") : BaseKey(), HasServices {
override fun bindServices(serviceBinder: ScopedServices.ServiceBinder) {
serviceBinder.add(AddOrEditTaskFragment.CONTROLLER_TAG, Injector.get().addOrEditTaskPresenter())
}
constructor(parent: Key) : this(parent, "")
@Parcelize
data class AddTaskKey(val parentKey: Key): AddOrEditTaskKey(parentKey)
data class AddTaskKey(val parentKey: Key): AddOrEditTaskKey(parentKey) {
override fun getScopeTag(): String = "AddTask"
}
@Parcelize
data class EditTaskKey(val parentKey: Key, val taskID: String): AddOrEditTaskKey(parentKey, taskID)
data class EditTaskKey(val parentKey: Key, val taskID: String): AddOrEditTaskKey(parentKey, taskID) {
override fun getScopeTag(): String = "EditTask[$taskID]"
}
override fun layout(): Int = R.layout.path_addoredittask
@@ -35,9 +46,7 @@ sealed class AddOrEditTaskKey(val parent: Key, val taskId: String = "") : BaseKe
override fun fabClickListener(fragment: Fragment): View.OnClickListener =
View.OnClickListener { v ->
val addOrEditTaskFragment = fragment as AddOrEditTaskFragment
if (addOrEditTaskFragment.saveTask()) {
addOrEditTaskFragment.navigateBack()
}
addOrEditTaskFragment.onSaveButtonClicked()
}
@@ -9,7 +9,6 @@ import com.zhuinden.simplestackdemoexamplefragments.presentation.objects.Task
import com.zhuinden.simplestackdemoexamplefragments.presentation.paths.tasks.TasksFragment
import com.zhuinden.simplestackdemoexamplefragments.presentation.paths.tasks.TasksKey
import com.zhuinden.simplestackdemoexamplefragments.util.BasePresenter
import com.zhuinden.simplestackdemoexamplefragments.util.BaseViewContract
import com.zhuinden.simplestackdemoexamplefragments.util.MessageQueue
import com.zhuinden.statebundle.StateBundle
import io.reactivex.android.schedulers.AndroidSchedulers
@@ -23,15 +22,7 @@ class AddOrEditTaskPresenter @Inject constructor(
private val taskRepository: TaskRepository,
private val messageQueue: MessageQueue,
private val backstack: Backstack
) : BasePresenter<AddOrEditTaskPresenter.ViewContract>(), Bundleable {
interface ViewContract: BaseViewContract {
fun setTitle(title: String)
fun setDescription(description: String)
fun hideKeyboard()
}
) : BasePresenter<AddOrEditTaskFragment>(), AddOrEditTaskFragment.Presenter, Bundleable {
var title: String? = null
var description: String? = null
@@ -48,7 +39,7 @@ class AddOrEditTaskPresenter @Inject constructor(
}
@SuppressLint("CheckResult")
override fun onAttach(view: ViewContract) {
override fun onAttach(view: AddOrEditTaskFragment) {
val addOrEditTaskKey: AddOrEditTaskKey = view.getKey()
taskId = addOrEditTaskKey.taskId
@@ -68,7 +59,21 @@ class AddOrEditTaskPresenter @Inject constructor(
}
}
override fun onDetach(view: ViewContract) {
override fun onDetach(view: AddOrEditTaskFragment) {
}
override fun onTitleChanged(title: String) {
updateTitle(title)
}
override fun onDescriptionChanged(description: String) {
updateDescription(description)
}
override fun onSaveButtonClicked() {
if (saveTask()) {
navigateBack()
}
}
override fun toBundle(): StateBundle = StateBundle().apply {
@@ -83,7 +88,7 @@ class AddOrEditTaskPresenter @Inject constructor(
}
}
fun saveTask(): Boolean {
private fun saveTask(): Boolean {
val task = task
val title = title
val description = description
@@ -100,7 +105,7 @@ class AddOrEditTaskPresenter @Inject constructor(
return false
}
fun navigateBack() {
private fun navigateBack() {
view!!.hideKeyboard()
val addOrEditTaskKey = view!!.getKey<AddOrEditTaskKey>()
when(addOrEditTaskKey) {
@@ -3,17 +3,28 @@ package com.zhuinden.simplestackdemoexamplefragments.presentation.paths.statisti
import com.zhuinden.simplestackdemoexamplefragments.R
import com.zhuinden.simplestackdemoexamplefragments.application.Injector
import com.zhuinden.simplestackdemoexamplefragments.util.BaseFragment
import com.zhuinden.simplestackdemoexamplefragments.util.MvpPresenter
import com.zhuinden.simplestackdemoexamplefragments.util.backstackDelegate
import kotlinx.android.synthetic.main.path_statistics.*
/**
* Created by Zhuinden on 2018. 08. 20.
*/
// UNSCOPED!
class StatisticsFragment : BaseFragment<StatisticsPresenter.ViewContract, StatisticsPresenter>(), StatisticsPresenter.ViewContract {
class StatisticsFragment : BaseFragment<StatisticsFragment, StatisticsFragment.Presenter>() {
private val myResources = Injector.get().resources()
private val statisticsPresenter = Injector.get().statisticsPresenter()
override val presenter: StatisticsPresenter = statisticsPresenter
companion object {
const val CONTROLLER_TAG = "AddOrEditTaskPresenter"
}
interface Presenter: MvpPresenter<StatisticsFragment> {
}
override val presenter: Presenter by lazy {
backstackDelegate.lookupService<StatisticsFragment.Presenter>(CONTROLLER_TAG)
}
override fun getThis(): StatisticsFragment = this
fun setProgressIndicator(active: Boolean) {
@@ -23,7 +34,7 @@ class StatisticsFragment : BaseFragment<StatisticsPresenter.ViewContract, Statis
}
}
override fun showStatistics(numberOfIncompleteTasks: Int, numberOfCompletedTasks: Int) {
fun showStatistics(numberOfIncompleteTasks: Int, numberOfCompletedTasks: Int) {
textStatistics.text = when {
numberOfCompletedTasks == 0 && numberOfIncompleteTasks == 0 -> myResources.getString(R.string.statistics_no_tasks)
else ->
@@ -2,15 +2,24 @@ package com.zhuinden.simplestackdemoexamplefragments.presentation.paths.statisti
import android.support.v4.app.Fragment
import android.view.View
import com.zhuinden.simplestack.ScopedServices
import com.zhuinden.simplestackdemoexamplefragments.R
import com.zhuinden.simplestackdemoexamplefragments.application.BaseKey
import com.zhuinden.simplestackdemoexamplefragments.application.Injector
import com.zhuinden.simplestackdemoexamplefragments.util.scopedservices.HasServices
import kotlinx.android.parcel.Parcelize
/**
* Created by Zhuinden on 2018. 08. 20.
*/
@Parcelize
data class StatisticsKey(val placeholder: String = "") : BaseKey() {
data class StatisticsKey(val placeholder: String = "") : BaseKey(), HasServices {
override fun bindServices(serviceBinder: ScopedServices.ServiceBinder) {
serviceBinder.add(StatisticsFragment.CONTROLLER_TAG, Injector.get().statisticsPresenter())
}
override fun getScopeTag(): String = "Statistics"
constructor() : this("")
override fun layout(): Int = R.layout.path_statistics
@@ -2,9 +2,7 @@ package com.zhuinden.simplestackdemoexamplefragments.presentation.paths.statisti
import com.zhuinden.simplestackdemoexamplefragments.data.repository.TaskRepository
import com.zhuinden.simplestackdemoexamplefragments.util.BasePresenter
import com.zhuinden.simplestackdemoexamplefragments.util.BaseViewContract
import com.zhuinden.simplestackdemoexamplefragments.util.combineTwo
import com.zhuinden.statebundle.StateBundle
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
@@ -16,14 +14,10 @@ import javax.inject.Inject
class StatisticsPresenter @Inject constructor(
private val taskRepository: TaskRepository
) : BasePresenter<StatisticsPresenter.ViewContract>() {
interface ViewContract: BaseViewContract {
fun showStatistics(numberOfIncompleteTasks: Int, numberOfCompletedTasks: Int)
}
) : BasePresenter<StatisticsFragment>(), StatisticsFragment.Presenter {
private lateinit var disposable: Disposable
override fun onAttach(view: StatisticsPresenter.ViewContract) {
override fun onAttach(view: StatisticsFragment) {
disposable = combineTwo(
taskRepository.activeTasksWithChanges,
taskRepository.completedTasksWithChanges)
@@ -34,12 +28,7 @@ class StatisticsPresenter @Inject constructor(
}
}
override fun onDetach(view: StatisticsPresenter.ViewContract) {
override fun onDetach(view: StatisticsFragment) {
disposable.dispose()
}
override fun toBundle(): StateBundle = StateBundle()
override fun fromBundle(bundle: StateBundle?) {
}
}
Oops, something went wrong.

0 comments on commit 725107e

Please sign in to comment.