de.mrapp.android.dialog.decorator.WizardDialogDecorator.java Source code

Java tutorial

Introduction

Here is the source code for de.mrapp.android.dialog.decorator.WizardDialogDecorator.java

Source

/*
 * Copyright 2014 - 2016 Michael Rapp
 *
 * 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 de.mrapp.android.dialog.decorator;

import android.os.Bundle;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.Button;
import android.widget.LinearLayout;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Set;

import de.mrapp.android.dialog.R;
import de.mrapp.android.dialog.WizardDialog;
import de.mrapp.android.dialog.WizardDialog.TabPosition;
import de.mrapp.android.dialog.WizardDialog.WizardListener;
import de.mrapp.android.dialog.adapter.ViewPagerAdapter;
import de.mrapp.android.dialog.datastructure.ViewPagerItem;
import de.mrapp.android.dialog.view.ViewPager;

import static de.mrapp.android.util.Condition.ensureAtLeast;
import static de.mrapp.android.util.Condition.ensureNotEmpty;
import static de.mrapp.android.util.Condition.ensureNotNull;

/**
 * A decorator, which allows to modify the view hierarchy of a dialog, which is designed according
 * to Android 5's Material Design guidelines even on pre-Lollipop devices and provides a navigation
 * for switching between multiple fragments.
 *
 * @author Michael Rapp
 * @since 3.2.0
 */
public class WizardDialogDecorator extends AbstractDialogFragmentDecorator<WizardDialog>
        implements de.mrapp.android.dialog.model.WizardDialogDecorator, OnPageChangeListener {

    /**
     * The name of the extra, which is used to store the position of the tabs, which indicate the
     * currently shown fragment, within a bundle.
     */
    private static final String TAB_POSITION_EXTRA = WizardDialogDecorator.class.getSimpleName() + "::tabPosition";

    /**
     * The name of the extra, which is used to store, whether the tabs, which indicate the currently
     * shown fragment, should be enabled, or not, within a bundle.
     */
    private static final String TAB_LAYOUT_ENABLED_EXTRA = WizardDialogDecorator.class.getSimpleName()
            + "::tabLayoutEnabled";

    /**
     * The name of the extra, which is used to store, whether the tabs, which indicate the currently
     * shown fragment, should be shown, or not, within a bundle.
     */
    private static final String TAB_LAYOUT_SHOWN_EXTRA = WizardDialogDecorator.class.getSimpleName()
            + "::tabLayoutShown";

    /**
     * The name of the extra, which is used to store the height of the indicator, which indicates
     * the currently shown fragment, within a bundle.
     */
    private static final String TAB_INDICATOR_HEIGHT_EXTRA = WizardDialogDecorator.class.getSimpleName()
            + "::tabIndicatorHeight";

    /**
     * The name of the extra, which is used to store the color of the indicator, which indicates the
     * currently shown fragment, within a bundle.
     */
    private static final String TAB_INDICATOR_COLOR_EXTRA = WizardDialogDecorator.class.getSimpleName()
            + "::tabIndicatorColor";

    /**
     * The name of the extra, which is used to store the color of the tabs, which indicate the
     * currently shown fragment, within a bundle.
     */
    private static final String TAB_TEXT_COLOR_EXTRA = WizardDialogDecorator.class.getSimpleName()
            + "::tabTextColor";

    /**
     * The name of the extra, which is used to store the selected text color of the tabs, which
     * indicate the currently shown fragment, within a bundle.
     */
    private static final String TAB_SELECTED_TEXT_COLOR_EXTRA = WizardDialogDecorator.class.getSimpleName()
            + "::tabSelectedTextColor";

    /**
     * The name of the extra, which is used to store, whether switching between fragments using
     * swipe gestures, should be enabled, or not, within a bundle.
     */
    private static final String SWIPE_ENABLED_EXTRA = WizardDialogDecorator.class.getSimpleName()
            + "::swipeEnabled";

    /**
     * The name of the extra, which is used to store, whether the dialog's buttons should be shown,
     * or not, within a bundle.
     */
    private static final String BUTTON_BAR_SHOWN_EXTRA = WizardDialogDecorator.class.getSimpleName()
            + "::buttonBarShown";

    /**
     * The name of the extra, which is used to store the color of the button texts of the dialog
     * within a bundle.
     */
    private static final String BUTTON_TEXT_COLOR_EXTRA = WizardDialogDecorator.class.getSimpleName()
            + "::buttonTextColor";

    /**
     * The name of the extra, which is used to store, whether the divider, which is located above
     * the dialog's buttons, should be shown, or not, within a bundle.
     */
    private static final String SHOW_BUTTON_BAR_DIVIDER_EXTRA = WizardDialogDecorator.class.getSimpleName()
            + "::showButtonBarDivider";

    /**
     * The name of the extra, which is used to store the color of the divider, which is located
     * above the dialog's buttons, within a bundle.
     */
    private static final String BUTTON_BAR_DIVIDER_COLOR_EXTRA = WizardDialogDecorator.class.getSimpleName()
            + "::buttonBarDividerColor";

    /**
     * The name of the extra, which is used to store the text of the back button of the dialog
     * within a bundle.
     */
    private static final String BACK_BUTTON_TEXT_EXTRA = WizardDialogDecorator.class.getSimpleName()
            + "::backButtonText";

    /**
     * The name of the extra, which is used to store the text of the next button of the dialog
     * within a bundle.
     */
    private static final String NEXT_BUTTON_TEXT_EXTRA = WizardDialogDecorator.class.getSimpleName()
            + "::nextButtonText";

    /**
     * The name of the extra, which is used to store the text of the finish button of the dialog
     * within a bundle.
     */
    private static final String FINISH_BUTTON_TEXT_EXTRA = WizardDialogDecorator.class.getSimpleName()
            + "::finishButtonText";

    /**
     * The name of the extra, which is used to store items of the dialog's view pager within a
     * bundle.
     */
    private static final String VIEW_PAGER_ITEMS_EXTRA = WizardDialogDecorator.class.getSimpleName()
            + "::viewPagerItems";

    /**
     * An array list, which contains the items if the dialog's view pager.
     */
    private final ArrayList<ViewPagerItem> viewPagerItems;

    /**
     * The listeners, which should be notified, when the user navigates within the dialog.
     */
    private final Set<WizardListener> wizardListeners;

    /**
     * The listeners, which should be notified, when the page of the dialog's view pager has been
     * changed.
     */
    private final Set<OnPageChangeListener> onPageChangeListeners;

    /**
     * The adapter, which is used to manage the dialog's fragments.
     */
    private ViewPagerAdapter viewPagerAdapter;

    /**
     * The view pager, which is used to show the dialog's fragments.
     */
    private ViewPager viewPager;

    /**
     * The tabs, which indicate the currently shown fragment.
     */
    private TabLayout tabLayout;

    /**
     * The parent view of the layout, which is used to show the dialog's buttons.
     */
    private ViewGroup buttonBarContainer;

    /**
     * The back button of the dialog.
     */
    private Button backButton;

    /**
     * The next button of the dialog.
     */
    private Button nextButton;

    /**
     * The finish button of the dialog.
     */
    private Button finishButton;

    /**
     * The divider, which is shown above the dialog's buttons.
     */
    private View buttonBarDivider;

    /**
     * The position of the tabs, which indicate the currently shown fragment.
     */
    private TabPosition tabPosition;

    /**
     * True, if the tabs, which indicate the currently shown fragment, are enabled, false
     * otherwise.
     */
    private boolean tabLayoutEnabled;

    /**
     * True, if the tabs, which indicate the currently shown fragment, are shown, false otherwise.
     */
    private boolean tabLayoutShown;

    /**
     * The height of the indicator, which indicates the currently shown fragment.
     */
    private int tabIndicatorHeight;

    /**
     * The color of the indicator, which indicates the currently shown fragment.
     */
    private int tabIndicatorColor;

    /**
     * The text color of the tabs, which indicate the currently shown fragment.
     */
    private int tabTextColor;

    /**
     * The selected text color of the tabs, which indicate the currently shown fragment.
     */
    private int tabSelectedTextColor;

    /**
     * True, if switching between fragments using swipe gestures is enabled, false otherwise.
     */
    private boolean swipeEnabled;

    /**
     * True, if the dialogs buttons are shown, false otherwise.
     */
    private boolean buttonBarShown;

    /**
     * The color of the button texts of the dialog.
     */
    private int buttonTextColor;

    /**
     * The text of the back button of the dialog.
     */
    private CharSequence backButtonText;

    /**
     * The text of the next button of the dialog.
     */
    private CharSequence nextButtonText;

    /**
     * The text of the finish button of the dialog.
     */
    private CharSequence finishButtonText;

    /**
     * True, if the divider, which is located above the dialog's buttons, is shown, false
     * otherwise.
     */
    private boolean showButtonBarDivider;

    /**
     * The color of the divider, which is located above the dialog's buttons.
     */
    private int buttonBarDividerColor;

    /**
     * Inflates the tab layout, which indicates the currently shown fragment.
     */
    private void inflateTabLayout() {
        if (getDialogRootView() != null) {
            LayoutInflater layoutInflater = LayoutInflater.from(getContext());
            ViewGroup headerContainer = (ViewGroup) getDialogRootView().findViewById(R.id.header);
            ViewGroup contentContainer = (ViewGroup) getDialogRootView().findViewById(R.id.content_container);

            if (tabLayout != null) {
                headerContainer.removeViewInLayout(tabLayout);
                contentContainer.removeView(tabLayout);
                tabLayout = null;
            }

            if (getDialog().isHeaderShown() && getTabPosition() != TabPosition.NO_HEADER
                    && ((TextUtils.isEmpty(getDialog().getTitle()) && TextUtils.isEmpty(getDialog().getMessage()))
                            || getTabPosition() == TabPosition.PREFER_HEADER)) {
                tabLayout = (TabLayout) layoutInflater.inflate(R.layout.wizard_dialog_tab_layout, headerContainer,
                        false);
                headerContainer.addView(tabLayout);
            } else {
                tabLayout = (TabLayout) layoutInflater.inflate(R.layout.wizard_dialog_tab_layout, contentContainer,
                        false);
                contentContainer.addView(tabLayout, 0);
            }

            tabLayout.setupWithViewPager(viewPager);
        }
    }

    /**
     * Inflates the layout, which is used to show the dialog's buttons.
     */
    private void inflateButtonBar() {
        ViewGroup rootView = (ViewGroup) getDialogRootView();

        if (rootView != null) {
            LayoutInflater layoutInflater = LayoutInflater.from(getContext());
            buttonBarContainer = (ViewGroup) layoutInflater.inflate(R.layout.button_bar_container, rootView, false);
            rootView.addView(buttonBarContainer);
            View view = layoutInflater.inflate(R.layout.horizontal_button_bar, buttonBarContainer, false);
            buttonBarContainer.addView(view);
            nextButton = (Button) view.findViewById(android.R.id.button1);
            finishButton = (Button) view.findViewById(android.R.id.button2);
            backButton = (Button) view.findViewById(android.R.id.button3);
            buttonBarDivider = view.findViewById(R.id.button_bar_divider);
        }
    }

    /**
     * Adapts the tab layout, which indicates the currently shown fragment.
     */
    private void adaptTabLayout() {
        adaptTabLayoutEnableState();
        adaptTabLayoutVisibility();
        adaptTabIndicatorHeight();
        adaptTabIndicatorColor();
        adaptTabTextColor();
    }

    /**
     * Adapts the enable state of the tab layout, which indicates the currently shown fragment.
     */
    private void adaptTabLayoutEnableState() {
        if (tabLayout != null) {
            LinearLayout tabStrip = ((LinearLayout) tabLayout.getChildAt(0));
            tabStrip.setEnabled(tabLayoutEnabled);

            for (int i = 0; i < tabStrip.getChildCount(); i++) {
                tabStrip.getChildAt(i).setEnabled(tabLayoutEnabled);
            }
        }
    }

    /**
     * Adapts the visibility of the tab layout, which indicates the currently shown fragment.
     */
    private void adaptTabLayoutVisibility() {
        if (tabLayout != null) {
            tabLayout.setVisibility(tabLayoutShown ? View.VISIBLE : View.GONE);
        }
    }

    /**
     * Adapts the height of the indicator, which indicates the currently shown fragment.
     */
    private void adaptTabIndicatorHeight() {
        if (tabLayout != null) {
            tabLayout.setSelectedTabIndicatorHeight(tabIndicatorHeight);
        }
    }

    /**
     * Adapts the color of the indicator, which indicates the currently shown fragment.
     */
    private void adaptTabIndicatorColor() {
        if (tabLayout != null) {
            tabLayout.setSelectedTabIndicatorColor(tabIndicatorColor);
        }
    }

    /**
     * Adapts the text color of the tabs, which indicate the currently shown fragment.
     */
    private void adaptTabTextColor() {
        if (tabLayout != null) {
            tabLayout.setTabTextColors(tabTextColor, tabSelectedTextColor);
        }
    }

    /**
     * Adapts the view pager, which is used to show the dialog's fragments.
     */
    private void adaptViewPager() {
        if (viewPager != null) {
            viewPager.enableSwipe(swipeEnabled);
        }
    }

    /**
     * Adapts the text color of the dialog's buttons.
     */
    private void adaptButtonTextColor() {
        if (backButton != null) {
            backButton.setTextColor(buttonTextColor);
        }

        if (nextButton != null) {
            nextButton.setTextColor(buttonTextColor);
        }

        if (finishButton != null) {
            finishButton.setTextColor(buttonTextColor);
        }
    }

    /**
     * Adapts the dialog's back button.
     */
    private void adaptBackButton() {
        if (backButton != null) {
            backButton.setText(backButtonText.toString().toUpperCase(Locale.getDefault()));
            backButton.setOnClickListener(createBackButtonListener());
        }
    }

    /**
     * Creates and returns a listener, which allows to show the previous fragment, when the
     * corresponding button is clicked.
     *
     * @return The listener, which has been created, as an instance of the type {@link
     * OnClickListener}
     */
    private OnClickListener createBackButtonListener() {
        return new OnClickListener() {

            @Override
            public void onClick(final View v) {
                int selectedIndex = viewPager.getCurrentItem();

                if (notifyOnPrevious(selectedIndex)) {
                    viewPager.setCurrentItem(selectedIndex - 1);
                }
            }

        };
    }

    /**
     * Adapts the dialog's next button.
     */
    private void adaptNextButton() {
        if (nextButton != null) {
            nextButton.setText(nextButtonText.toString().toUpperCase(Locale.getDefault()));
            nextButton.setOnClickListener(createNextButtonListener());
        }
    }

    /**
     * Creates and returns a listener, which allows to show the next fragment, when the
     * corresponding button is clicked.
     *
     * @return The listener, which has been created, as an instance of the type {@link
     * OnClickListener}
     */
    private OnClickListener createNextButtonListener() {
        return new OnClickListener() {

            @Override
            public void onClick(final View v) {
                int selectedIndex = viewPager.getCurrentItem();

                if (notifyOnNext(selectedIndex)) {
                    viewPager.setCurrentItem(selectedIndex + 1);
                }
            }

        };
    }

    /**
     * Adapts the dialog's finish button.
     */
    private void adaptFinishButton() {
        if (finishButton != null) {
            finishButton.setText(finishButtonText.toString().toUpperCase(Locale.getDefault()));
            finishButton.setOnClickListener(createFinishButtonListener());
        }
    }

    /**
     * Creates and returns a listener, which allows to close the dialog, when the corresponding
     * button is clicked.
     *
     * @return The listener, which has been created, as an instance of the type {@link
     * OnClickListener}
     */
    private OnClickListener createFinishButtonListener() {
        return new OnClickListener() {

            @Override
            public void onClick(final View v) {
                int selectedIndex = viewPager.getCurrentItem();

                if (notifyOnFinish(selectedIndex)) {
                    getDialog().dismiss();
                }
            }

        };
    }

    /**
     * Adapts the visibility of the dialog's buttons.
     */
    private void adaptButtonBarVisibility() {
        if (buttonBarContainer != null) {
            buttonBarContainer.setVisibility(buttonBarShown ? View.VISIBLE : View.GONE);
        }
    }

    /**
     * Adapts the visibility of the divider, which is shown above the dialog's buttons.
     */
    private void adaptButtonBarDividerVisibility() {
        if (buttonBarDivider != null) {
            buttonBarDivider.setVisibility(showButtonBarDivider ? View.VISIBLE : View.GONE);
        }
    }

    /**
     * Adapts the color of the divider, which is shown above the dialog's buttons.
     */
    private void adaptButtonBarDividerColor() {
        if (buttonBarDivider != null) {
            buttonBarDivider.setBackgroundColor(buttonBarDividerColor);
        }
    }

    /**
     * Adapts the visibility of the dialog's buttons, depending on the currently shown fragment.
     */
    private void adaptButtonVisibility() {
        if (viewPager != null && viewPagerAdapter != null && backButton != null && nextButton != null
                && finishButton != null) {
            int selectedIndex = viewPager.getCurrentItem();
            backButton.setVisibility(selectedIndex > 0 ? View.VISIBLE : View.GONE);
            nextButton.setVisibility(selectedIndex < viewPagerAdapter.getCount() - 1 ? View.VISIBLE : View.GONE);
            finishButton.setVisibility(selectedIndex == viewPagerAdapter.getCount() - 1 ? View.VISIBLE : View.GONE);
        }
    }

    /**
     * Notifies all listeners, when the next fragment is about to be shown.
     *
     * @param index
     *         The index of the fragment, which is about to be shown, as an {@link Integer} value
     * @return True, if the fragment is allowed to be shown, false otherwise
     */
    private boolean notifyOnNext(final int index) {
        boolean result = true;

        for (WizardListener listener : wizardListeners) {
            result &= listener.onNext(index, viewPagerAdapter.getItem(index));
        }

        return result;
    }

    /**
     * Notifies all listeners, when the previous fragment is about to be shown.
     *
     * @param index
     *         The index of the fragment, which is about to be shown, as an {@link Integer} value
     * @return True, if the fragment is allowed to be shown, false otherwise
     */
    private boolean notifyOnPrevious(final int index) {
        boolean result = true;

        for (WizardListener listener : wizardListeners) {
            result &= listener.onPrevious(index, viewPagerAdapter.getItem(index));
        }

        return result;
    }

    /**
     * Notifies all listeners, when the last fragment is about to be finished.
     *
     * @param index
     *         The index of the fragment, which is about to be finished, as an {@link Integer}
     *         value
     * @return True, if the last fragment is allowed to be finished, false otherwise
     */
    private boolean notifyOnFinish(final int index) {
        boolean result = true;

        for (WizardListener listener : wizardListeners) {
            result &= listener.onFinish(index, viewPagerAdapter.getItem(index));
        }

        return result;
    }

    /**
     * Creates a new decorator, which allows to modify the view hierarchy of a dialog, which is
     * designed according to Android 5's Material Design guidelines even on pre-Lollipop devices and
     * provides a navigation for switching between multiple fragments..
     *
     * @param dialog
     *         The dialog, whose view hierarchy should be modified by the decorator, as an instance
     *         of the generic type DialogType. The dialog may not be null
     */
    public WizardDialogDecorator(@NonNull final WizardDialog dialog) {
        super(dialog);
        this.viewPagerItems = new ArrayList<>();
        this.wizardListeners = new LinkedHashSet<>();
        this.onPageChangeListeners = new LinkedHashSet<>();
    }

    @Override
    public final ViewPager getViewPager() {
        return viewPager;
    }

    @Override
    public final TabLayout getTabLayout() {
        return tabLayout;
    }

    @Override
    public final Button getBackButton() {
        return backButton;
    }

    @Override
    public final Button getNextButton() {
        return nextButton;
    }

    @Override
    public final Button getFinishButton() {
        return finishButton;
    }

    @Override
    public final void addFragment(@NonNull final Class<? extends Fragment> fragmentClass) {
        addFragment(fragmentClass, null);
    }

    @Override
    public final void addFragment(@NonNull final Class<? extends Fragment> fragmentClass,
            @Nullable final Bundle arguments) {
        addFragment(null, fragmentClass, arguments);
    }

    @Override
    public final void addFragment(@StringRes final int resourceId,
            @NonNull final Class<? extends Fragment> fragmentClass) {
        addFragment(resourceId, fragmentClass, null);
    }

    @Override
    public final void addFragment(@StringRes final int resourceId,
            @NonNull final Class<? extends Fragment> fragmentClass, @Nullable final Bundle arguments) {
        addFragment(getContext().getText(resourceId), fragmentClass, arguments);
    }

    @Override
    public final void addFragment(@Nullable final CharSequence title,
            @NonNull final Class<? extends Fragment> fragmentClass) {
        addFragment(title, fragmentClass, null);
    }

    @Override
    public final void addFragment(@Nullable final CharSequence title,
            @NonNull final Class<? extends Fragment> fragmentClass, @Nullable final Bundle arguments) {
        ensureNotNull(fragmentClass, "The fragment class may not be null");
        viewPagerItems.add(new ViewPagerItem(title, fragmentClass, arguments));

        if (viewPagerAdapter != null) {
            viewPagerAdapter.addItem(title, fragmentClass, arguments);
        }
    }

    @Override
    public final void removeFragment(final int index) {
        viewPagerItems.remove(index);

        if (viewPagerAdapter != null) {
            viewPagerAdapter.removeItem(index);
        }
    }

    @Override
    public final void clearFragments() {
        viewPagerItems.clear();

        if (viewPagerAdapter != null) {
            viewPagerAdapter.clear();
        }
    }

    @Override
    public final int indexOfFragment(@NonNull final Class<? extends Fragment> fragmentClass) {
        ensureNotNull(fragmentClass, "The fragment class may not be null");

        for (int i = 0; i < viewPagerItems.size(); i++) {
            ViewPagerItem item = viewPagerItems.get(i);

            if (item.getFragmentClass().equals(fragmentClass)) {
                return i;
            }
        }

        return -1;
    }

    @Override
    public final int getFragmentCount() {
        return viewPagerItems.size();
    }

    @Override
    public final TabPosition getTabPosition() {
        return tabPosition;
    }

    @Override
    public final void setTabPosition(@NonNull final TabPosition tabPosition) {
        ensureNotNull(tabPosition, "The tab position may not be null");
        this.tabPosition = tabPosition;
        inflateTabLayout();
        adaptTabLayout();
    }

    @Override
    public final boolean isTabLayoutEnabled() {
        return tabLayoutEnabled;
    }

    @Override
    public final void enableTabLayout(final boolean enable) {
        this.tabLayoutEnabled = enable;
        adaptTabLayoutEnableState();
    }

    @Override
    public final boolean isTabLayoutShown() {
        return tabLayoutShown;
    }

    @Override
    public final void showTabLayout(final boolean show) {
        this.tabLayoutShown = show;
        adaptTabLayoutVisibility();
    }

    @Override
    public final int getTabIndicatorHeight() {
        return tabIndicatorHeight;
    }

    @Override
    public final void setTabIndicatorHeight(final int height) {
        ensureAtLeast(height, 1, "The height must be at least 1");
        this.tabIndicatorHeight = height;
        adaptTabIndicatorHeight();
    }

    @Override
    public final int getTabIndicatorColor() {
        return tabIndicatorColor;
    }

    @Override
    public final void setTabIndicatorColor(@ColorInt final int color) {
        this.tabIndicatorColor = color;
        adaptTabIndicatorColor();
    }

    @Override
    public final int getTabTextColor() {
        return tabTextColor;
    }

    @Override
    public final void setTabTextColor(@ColorInt final int textColor) {
        this.tabTextColor = textColor;
        adaptTabTextColor();
    }

    @Override
    public final int getTabSelectedTextColor() {
        return tabSelectedTextColor;
    }

    @Override
    public final void setTabSelectedTextColor(@ColorInt final int selectedTextColor) {
        this.tabSelectedTextColor = selectedTextColor;
        adaptTabTextColor();
    }

    @Override
    public final boolean isSwipeEnabled() {
        return swipeEnabled;
    }

    @Override
    public final void enableSwipe(final boolean enable) {
        this.swipeEnabled = enable;
        adaptViewPager();
    }

    @Override
    public final boolean isButtonBarShown() {
        return buttonBarShown;
    }

    @Override
    public final void showButtonBar(final boolean show) {
        this.buttonBarShown = show;
        adaptButtonBarVisibility();
    }

    @Override
    public final int getButtonTextColor() {
        return buttonTextColor;
    }

    @Override
    public final void setButtonTextColor(@ColorInt final int color) {
        this.buttonTextColor = color;
        adaptButtonTextColor();
    }

    @Override
    public final boolean isButtonBarDividerShown() {
        return showButtonBarDivider;
    }

    @Override
    public final void showButtonBarDivider(final boolean show) {
        this.showButtonBarDivider = show;
        adaptButtonBarDividerVisibility();
    }

    @Override
    public final int getButtonBarDividerColor() {
        return buttonBarDividerColor;
    }

    @Override
    public final void setButtonBarDividerColor(final int color) {
        this.buttonBarDividerColor = color;
        adaptButtonBarDividerColor();
    }

    @Override
    public final CharSequence getBackButtonText() {
        return backButtonText;
    }

    @Override
    public final void setBackButtonText(@StringRes final int resourceId) {
        setBackButtonText(getContext().getText(resourceId));
    }

    @Override
    public final void setBackButtonText(@NonNull final CharSequence text) {
        ensureNotNull(text, "The text may not be null");
        ensureNotEmpty(text, "The text may not be empty");
        this.backButtonText = text;
        adaptBackButton();
    }

    @Override
    public final CharSequence getNextButtonText() {
        return nextButtonText;
    }

    @Override
    public final void setNextButtonText(@StringRes final int resourceId) {
        setNextButtonText(getContext().getText(resourceId));
    }

    @Override
    public final void setNextButtonText(@NonNull final CharSequence text) {
        ensureNotNull(text, "The text may not be null");
        ensureNotEmpty(text, "The text may not be empty");
        this.nextButtonText = text;
        adaptNextButton();
    }

    @Override
    public final CharSequence getFinishButtonText() {
        return finishButtonText;
    }

    @Override
    public final void setFinishButtonText(@StringRes final int resourceId) {
        setFinishButtonText(getContext().getText(resourceId));
    }

    @Override
    public final void setFinishButtonText(@NonNull final CharSequence text) {
        ensureNotNull(text, "The text may not be null");
        ensureNotEmpty(text, "The text may not be empty");
        this.finishButtonText = text;
        adaptFinishButton();
    }

    @Override
    public final void addWizardListener(@NonNull final WizardListener listener) {
        ensureNotNull(listener, "The listener may not be null");
        wizardListeners.add(listener);
    }

    @Override
    public final void removeWizardListener(@NonNull final WizardListener listener) {
        ensureNotNull(listener, "The listener may not be null");
        wizardListeners.remove(listener);
    }

    @Override
    public final void addOnPageChangeListener(@NonNull final OnPageChangeListener listener) {
        ensureNotNull(listener, "The listener may not be null");
        onPageChangeListeners.add(listener);

        if (viewPager != null) {
            viewPager.addOnPageChangeListener(listener);
        }
    }

    @Override
    public final void removeOnPageChangeListener(@NonNull final OnPageChangeListener listener) {
        ensureNotNull(listener, "The listener may not be null");
        onPageChangeListeners.remove(listener);

        if (viewPager != null) {
            viewPager.removeOnPageChangeListener(listener);
        }
    }

    @Override
    public final void onPageScrolled(final int position, final float positionOffset,
            final int positionOffsetPixels) {

    }

    @Override
    public final void onPageSelected(final int position) {
        adaptButtonVisibility();
    }

    @Override
    public final void onPageScrollStateChanged(final int state) {

    }

    @Override
    public final void onSaveInstanceState(@NonNull final Bundle outState) {
        outState.putInt(TAB_POSITION_EXTRA, getTabPosition().getValue());
        outState.putBoolean(TAB_LAYOUT_ENABLED_EXTRA, isTabLayoutEnabled());
        outState.putBoolean(TAB_LAYOUT_SHOWN_EXTRA, isTabLayoutShown());
        outState.putInt(TAB_INDICATOR_HEIGHT_EXTRA, getTabIndicatorHeight());
        outState.putInt(TAB_INDICATOR_COLOR_EXTRA, getTabIndicatorColor());
        outState.putInt(TAB_TEXT_COLOR_EXTRA, getTabTextColor());
        outState.putInt(TAB_SELECTED_TEXT_COLOR_EXTRA, getTabSelectedTextColor());
        outState.putBoolean(SWIPE_ENABLED_EXTRA, isSwipeEnabled());
        outState.putBoolean(BUTTON_BAR_SHOWN_EXTRA, isButtonBarShown());
        outState.putInt(BUTTON_TEXT_COLOR_EXTRA, getButtonTextColor());
        outState.putBoolean(SHOW_BUTTON_BAR_DIVIDER_EXTRA, isButtonBarDividerShown());
        outState.putInt(BUTTON_BAR_DIVIDER_COLOR_EXTRA, getButtonBarDividerColor());
        outState.putCharSequence(BACK_BUTTON_TEXT_EXTRA, getBackButtonText());
        outState.putCharSequence(NEXT_BUTTON_TEXT_EXTRA, getNextButtonText());
        outState.putCharSequence(FINISH_BUTTON_TEXT_EXTRA, getFinishButtonText());
        outState.putParcelableArrayList(VIEW_PAGER_ITEMS_EXTRA,
                getDialog().getRetainInstance() ? null : viewPagerItems);
    }

    @Override
    public final void onRestoreInstanceState(@NonNull final Bundle savedInstanceState) {
        setTabPosition(TabPosition.fromValue(savedInstanceState.getInt(TAB_POSITION_EXTRA)));
        enableTabLayout(savedInstanceState.getBoolean(TAB_LAYOUT_ENABLED_EXTRA));
        showTabLayout(savedInstanceState.getBoolean(TAB_LAYOUT_SHOWN_EXTRA));
        setTabIndicatorHeight(savedInstanceState.getInt(TAB_INDICATOR_HEIGHT_EXTRA));
        setTabIndicatorColor(savedInstanceState.getInt(TAB_INDICATOR_COLOR_EXTRA));
        setTabTextColor(savedInstanceState.getInt(TAB_TEXT_COLOR_EXTRA));
        setTabSelectedTextColor(savedInstanceState.getInt(TAB_SELECTED_TEXT_COLOR_EXTRA));
        enableSwipe(savedInstanceState.getBoolean(SWIPE_ENABLED_EXTRA));
        showButtonBar(savedInstanceState.getBoolean(BUTTON_BAR_SHOWN_EXTRA));
        setButtonTextColor(savedInstanceState.getInt(BUTTON_TEXT_COLOR_EXTRA));
        showButtonBarDivider(savedInstanceState.getBoolean(SHOW_BUTTON_BAR_DIVIDER_EXTRA));
        setButtonBarDividerColor(savedInstanceState.getInt(BUTTON_BAR_DIVIDER_COLOR_EXTRA));
        CharSequence backButtonText = savedInstanceState.getCharSequence(BACK_BUTTON_TEXT_EXTRA);
        CharSequence nextButtonText = savedInstanceState.getCharSequence(NEXT_BUTTON_TEXT_EXTRA);
        CharSequence finishButtonText = savedInstanceState.getCharSequence(FINISH_BUTTON_TEXT_EXTRA);

        if (!TextUtils.isEmpty(backButtonText)) {
            setBackButtonText(backButtonText);
        }

        if (!TextUtils.isEmpty(nextButtonText)) {
            setNextButtonText(nextButtonText);
        }

        if (!TextUtils.isEmpty(finishButtonText)) {
            setFinishButtonText(finishButtonText);
        }

        ArrayList<ViewPagerItem> viewPagerItems = savedInstanceState.getParcelableArrayList(VIEW_PAGER_ITEMS_EXTRA);

        if (viewPagerItems != null) {
            for (ViewPagerItem item : viewPagerItems) {
                addFragment(item.getTitle(), item.getFragmentClass(), item.getArguments());
            }
        }
    }

    @Override
    protected final void onAttach(@NonNull final Window window, @NonNull final View view,
            @NonNull final FragmentManager fragmentManager) {
        View viewPagerView = view.findViewById(R.id.view_pager);

        if (viewPagerView instanceof ViewPager) {
            viewPagerAdapter = new ViewPagerAdapter(getContext(), fragmentManager, viewPagerItems);
            viewPager = (ViewPager) viewPagerView;
            viewPager.addOnPageChangeListener(this);

            for (OnPageChangeListener listener : onPageChangeListeners) {
                viewPager.addOnPageChangeListener(listener);
            }

            viewPager.setAdapter(viewPagerAdapter);
            inflateTabLayout();
            adaptTabLayout();
            adaptViewPager();
            inflateButtonBar();
            adaptButtonTextColor();
            adaptBackButton();
            adaptNextButton();
            adaptFinishButton();
            adaptButtonBarVisibility();
            adaptButtonBarDividerVisibility();
            adaptButtonBarDividerColor();
            adaptButtonVisibility();
        }
    }

    @Override
    protected final void onDetach() {
        tabLayout = null;
        viewPager = null;
        viewPagerAdapter = null;
        buttonBarContainer = null;
        backButton = null;
        nextButton = null;
        finishButton = null;
        buttonBarDivider = null;
    }

}