com.hannesdorfmann.mosby3.mvp.delegate.FragmentMvpDelegateImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.hannesdorfmann.mosby3.mvp.delegate.FragmentMvpDelegateImpl.java

Source

/*
 * Copyright 2015 Hannes Dorfmann.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hannesdorfmann.mosby3.mvp.delegate;

import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.BackstackAccessor;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.View;
import com.hannesdorfmann.mosby3.PresenterManager;
import com.hannesdorfmann.mosby3.mvp.MvpPresenter;
import com.hannesdorfmann.mosby3.mvp.MvpView;
import java.util.UUID;

/**
 * * The default implementation of {@link FragmentMvpDelegate}
 *
 * @param <V> The type of {@link MvpView}
 * @param <P> The type of {@link MvpPresenter}
 * @author Hannes Dorfmann
 * @see FragmentMvpDelegate
 * @since 1.1.0
 */
public class FragmentMvpDelegateImpl<V extends MvpView, P extends MvpPresenter<V>>
        implements FragmentMvpDelegate<V, P> {

    protected static final String KEY_MOSBY_VIEW_ID = "com.hannesdorfmann.mosby3.fragment.mvp.id";

    public static boolean DEBUG = false;
    private static final String DEBUG_TAG = "FragmentMvpVSDelegate";

    private MvpDelegateCallback<V, P> delegateCallback;
    protected Fragment fragment;
    protected final boolean keepPresenterInstanceDuringScreenOrientationChanges;
    protected final boolean keepPresenterOnBackstack;
    private boolean onViewCreatedCalled = false;
    protected String mosbyViewId;

    /**
     * @param fragment The Fragment
     * @param delegateCallback the DelegateCallback
     * @param keepPresenterDuringScreenOrientationChange true, if the presenter should be kept during
     * screen orientation
     * changes. Otherwise, false
     * @param keepPresenterOnBackstack true, if the presenter should be kept when the fragment is
     * destroyed because it is put on the backstack, Otherwise false
     */
    public FragmentMvpDelegateImpl(@NonNull Fragment fragment, @NonNull MvpDelegateCallback<V, P> delegateCallback,
            boolean keepPresenterDuringScreenOrientationChange, boolean keepPresenterOnBackstack) {
        if (delegateCallback == null) {
            throw new NullPointerException("MvpDelegateCallback is null!");
        }

        if (fragment == null) {
            throw new NullPointerException("Fragment is null!");
        }

        if (!keepPresenterDuringScreenOrientationChange && keepPresenterOnBackstack) {
            throw new IllegalArgumentException("It is not possible to keep the presenter on backstack, "
                    + "but NOT keep presenter through screen orientation changes. Keep presenter on backstack also "
                    + "requires keep presenter through screen orientation changes to be enabled");
        }

        this.fragment = fragment;
        this.delegateCallback = delegateCallback;
        this.keepPresenterInstanceDuringScreenOrientationChanges = keepPresenterDuringScreenOrientationChange;
        this.keepPresenterOnBackstack = keepPresenterOnBackstack;
    }

    /**
     * Generates the unique (mosby internal) view id and calls {@link
     * MvpDelegateCallback#createPresenter()}
     * to create a new presenter instance
     *
     * @return The new created presenter instance
     */
    private P createViewIdAndCreatePresenter() {

        P presenter = delegateCallback.createPresenter();
        if (presenter == null) {
            throw new NullPointerException(
                    "Presenter returned from createPresenter() is null. Activity is " + getActivity());
        }
        if (keepPresenterInstanceDuringScreenOrientationChanges) {
            mosbyViewId = UUID.randomUUID().toString();
            PresenterManager.putPresenter(getActivity(), mosbyViewId, presenter);
        }
        return presenter;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle bundle) {

        P presenter = null;

        if (bundle != null && keepPresenterInstanceDuringScreenOrientationChanges) {

            mosbyViewId = bundle.getString(KEY_MOSBY_VIEW_ID);

            if (DEBUG) {
                Log.d(DEBUG_TAG,
                        "MosbyView ID = " + mosbyViewId + " for MvpView: " + delegateCallback.getMvpView());
            }

            if (mosbyViewId != null
                    && (presenter = PresenterManager.getPresenter(getActivity(), mosbyViewId)) != null) {
                //
                // Presenter restored from cache
                //
                if (DEBUG) {
                    Log.d(DEBUG_TAG,
                            "Reused presenter " + presenter + " for view " + delegateCallback.getMvpView());
                }
            } else {
                //
                // No presenter found in cache, most likely caused by process death
                //
                presenter = createViewIdAndCreatePresenter();
                if (DEBUG) {
                    Log.d(DEBUG_TAG,
                            "No presenter found although view Id was here: " + mosbyViewId
                                    + ". Most likely this was caused by a process death. New Presenter created"
                                    + presenter + " for view " + getMvpView());
                }
            }
        } else {
            //
            // Activity starting first time, so create a new presenter
            //
            presenter = createViewIdAndCreatePresenter();
            if (DEBUG) {
                Log.d(DEBUG_TAG, "New presenter " + presenter + " for view " + getMvpView());
            }
        }

        if (presenter == null) {
            throw new IllegalStateException(
                    "Oops, Presenter is null. This seems to be a Mosby internal bug. Please report this issue here: https://github.com/sockeqwe/mosby/issues");
        }

        delegateCallback.setPresenter(presenter);
        getPresenter().attachView(getMvpView());

        if (DEBUG) {
            Log.d(DEBUG_TAG, "View" + getMvpView() + " attached to Presenter " + presenter);
        }

        onViewCreatedCalled = true;
    }

    @NonNull
    private Activity getActivity() {
        Activity activity = fragment.getActivity();
        if (activity == null) {
            throw new NullPointerException(
                    "Activity returned by Fragment.getActivity() is null. Fragment is " + fragment);
        }

        return activity;
    }

    private P getPresenter() {
        P presenter = delegateCallback.getPresenter();
        if (presenter == null) {
            throw new NullPointerException("Presenter returned from getPresenter() is null");
        }
        return presenter;
    }

    private V getMvpView() {
        V view = delegateCallback.getMvpView();
        if (view == null) {
            throw new NullPointerException("View returned from getMvpView() is null");
        }
        return view;
    }

    protected boolean retainPresenterInstance() {

        Activity activity = getActivity();
        if (activity.isChangingConfigurations()) {
            return keepPresenterInstanceDuringScreenOrientationChanges;
        }

        if (activity.isFinishing()) {
            return false;
        }

        if (keepPresenterOnBackstack && BackstackAccessor.isFragmentOnBackStack(fragment)) {
            return true;
        }

        return !fragment.isRemoving();
    }

    @Override
    public void onDestroyView() {

        onViewCreatedCalled = false;

        Activity activity = getActivity();
        boolean retainPresenterInstance = retainPresenterInstance();

        P presenter = getPresenter();
        presenter.detachView(retainPresenterInstance);
        if (!retainPresenterInstance && mosbyViewId != null) { // mosbyViewId is null if keepPresenterInstanceDuringScreenOrientationChanges  == false
            PresenterManager.remove(activity, mosbyViewId);
        }

        if (DEBUG) {
            Log.d(DEBUG_TAG, "detached MvpView from Presenter. MvpView " + delegateCallback.getMvpView()
                    + "   Presenter: " + presenter);
            Log.d(DEBUG_TAG,
                    "Retaining presenter instance: " + Boolean.toString(retainPresenterInstance) + " " + presenter);
        }
    }

    @Override
    public void onPause() {

    }

    @Override
    public void onResume() {

    }

    @Override
    public void onStart() {

        if (!onViewCreatedCalled) {
            throw new IllegalStateException("It seems that you are using "
                    + delegateCallback.getClass().getCanonicalName()
                    + " as headless (UI less) fragment (because onViewCreated() has not been called or maybe delegation misses that part). Having a Presenter without a View (UI) doesn't make sense. Simply use an usual fragment instead of an MvpFragment if you want to use a UI less Fragment");
        }
    }

    @Override
    public void onStop() {

    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {

    }

    @Override
    public void onAttach(Activity activity) {

    }

    @Override
    public void onDetach() {

    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        if ((keepPresenterInstanceDuringScreenOrientationChanges || keepPresenterOnBackstack) && outState != null) {
            outState.putString(KEY_MOSBY_VIEW_ID, mosbyViewId);

            if (DEBUG) {
                Log.d(DEBUG_TAG, "Saving MosbyViewId into Bundle. ViewId: " + mosbyViewId);
            }
        }
    }

    @Override
    public void onCreate(Bundle saved) {
    }

    @Override
    public void onDestroy() {
    }
}