Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle FragmentDialog displayed by a controller #28

Open
mradzinski opened this issue Sep 8, 2017 · 1 comment
Open

Handle FragmentDialog displayed by a controller #28

mradzinski opened this issue Sep 8, 2017 · 1 comment

Comments

@mradzinski
Copy link

Hey @sockeqwe, I have an use case where I need to display a FragmentDialog through a controller. I've noticed that there's a PR on the conductor repo to include a DialogController.

How would you handle such situations using mosby-conductor? I guess that following the MVP pattern the FragmentDialog shouldn't have much logic and that the logic should be handled by a presenter, but I can't seem to think of a way of having a presenter attached to it.

@mradzinski
Copy link
Author

mradzinski commented Sep 8, 2017

Based on that PR I linked above, I'm wondering if something like this might work...

MvpRestoreViewOnCreateController (Based on your MvpController class):

abstract class MvpRestoreViewOnCreateController<V : MvpView, P : MvpPresenter<V>>(args: Bundle?) :
        RestoreViewOnCreateController(args), MvpView, MvpConductorDelegateCallback<V, P> {

    private var presenter: P? = null
    
    init {
        addLifecycleListener(getMosbyLifecycleListener());
    }
    
    constructor(): this(null)

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedViewState: Bundle?): View {
        throw UnsupportedOperationException("not implemented")
    }

    override fun getMvpView(): V {
        return this as V
    }

    override fun setPresenter(presenter: P) {
        this.presenter = presenter
    }

    override fun getPresenter(): P? {
        return presenter
    }

    /**
     * This method is for internal purpose only.
     *
     * **Do not override this until you have a very good reason**
     * @return Mosby's lifecycle listener so that
     */
    private fun getMosbyLifecycleListener(): Controller.LifecycleListener {
        return MvpConductorLifecycleListener(this)
    }
}

And then BaseMvpDialogController based on the PR but extending MvpRestoreViewOnCreateController

/**
 * A controller that displays a dialog window, floating on top of its activity's window.
 * This is a wrapper over [Dialog] object like [android.app.DialogFragment].
 * Implementations should override this class and implement [onCreateDialog] to create a custom
 * dialog, such as an [android.app.AlertDialog]
 */
abstract class MvpDialogController<V : MvpView, P : MvpPresenter<V>>(args: Bundle?) :
        RestoreViewOnCreateController<V, P>(args) {

    companion object {
        private val SAVED_DIALOG_STATE_TAG = "android:savedDialogState"
    }

    @get:Nullable
    private lateinit var dialog: Dialog
        private set

    private var dismissed: Boolean = false

    /**
     * Convenience constructor for use when no arguments are needed.
     */
    protected constructor() : this(null)

    @NonNull
    override fun onCreateView(@NonNull inflater: LayoutInflater, @NonNull container: ViewGroup, @Nullable savedViewState: Bundle?): View {
        dialog = onCreateDialog(savedViewState)

        dialog.ownerActivity = activity
        dialog.setOnDismissListener({ dismissDialog() })
        if (savedViewState != null) {
            val dialogState = savedViewState.getBundle(SAVED_DIALOG_STATE_TAG)
            if (dialogState != null) {
                dialog.onRestoreInstanceState(dialogState)
            }
        }

        return View(activity) //stub view
    }

    override fun onSaveViewState(@NonNull view: View, @NonNull outState: Bundle) {
        super.onSaveViewState(view, outState)
        val dialogState = dialog.onSaveInstanceState()
        outState.putBundle(SAVED_DIALOG_STATE_TAG, dialogState)
    }

    override fun onAttach(@NonNull view: View) {
        super.onAttach(view)
        dialog.show()
    }

    override fun onDetach(@NonNull view: View) {
        super.onDetach(view)
        dialog.hide()
    }

    override fun onDestroyView(@NonNull view: View) {
        super.onDestroyView(view)
        dialog.setOnDismissListener(null)
        dialog.dismiss()
    }

    /**
     * Display the dialog, create a transaction and pushing the controller.
     * @param router The router on which the transaction will be applied
     * @param tag The tag for this controller
     */
    fun showDialog(@NonNull router: Router, @Nullable tag: String) {
        dismissed = false
        router.pushController(RouterTransaction.with(this)
                .pushChangeHandler(VerticalChangeHandler(150, false))
                .popChangeHandler(VerticalChangeHandler(150, false))
                .tag(tag))
    }

    /**
     * Dismiss the dialog and pop this controller
     */
    @Suppress("MemberVisibilityCanPrivate")
    fun dismissDialog() {
        if (dismissed) {
            return
        }
        router.popController(this)
        dismissed = true
    }

    /**
     * Build your own custom Dialog container such as an [android.app.AlertDialog]
     *
     * @param savedViewState A bundle for the view's state, which would have been created in [onSaveViewState] or `null` if no saved state exists.
     * @return Return a new Dialog instance to be displayed by the Controller
     */
    @NonNull
    protected abstract fun onCreateDialog(@Nullable savedViewState: Bundle?): Dialog
}

Let me know you thoughts mate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant