de.swm.commons.mobile.client.presenter.AbstractMobilePresenter.java Source code

Java tutorial

Introduction

Here is the source code for de.swm.commons.mobile.client.presenter.AbstractMobilePresenter.java

Source

/*
 * Copyright 2011 SWM Services GmbH.
 * 
 * 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.swm.commons.mobile.client.presenter;

import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.HasWidgets;
import de.swm.commons.mobile.client.page.SimplePage;
import de.swm.commons.mobile.client.page.Transition;
import de.swm.commons.mobile.client.utils.AsyncCallbackSuccess;
import de.swm.commons.mobile.client.widgets.LoadingIndicatorPopup;
import de.swm.commons.mobile.client.widgets.itf.ISpinnerStarted;
import de.swm.commons.mobile.client.widgets.page.IMainView;
import de.swm.commons.mobile.client.widgets.page.IPageWithHeader;
import de.swm.commons.mobile.client.widgets.page.IPageWithoutHeader;
import de.swm.gwt.client.eventbus.IDispatcher;
import de.swm.gwt.client.eventbus.IEvent;
import de.swm.gwt.client.eventbus.MobileEvent;
import de.swm.gwt.client.mobile.Direction;
import de.swm.gwt.client.mobile.IPage;
import de.swm.gwt.client.mobile.ITransitionCompletedCallback;

import java.util.logging.Logger;

/**
 * Abstract presenter class.
 */
public abstract class AbstractMobilePresenter implements IMobilePresenter {

    private static final Logger LOGGER = Logger.getLogger(AbstractMobilePresenter.class.getName());

    private static final LoadingIndicatorPopup spinner = new LoadingIndicatorPopup();

    /**
     * The global content area is null or contains a fixed toolbar on the button of the page. *
     */
    private static HasWidgets globalContentArea;

    private static Direction nextTransitionDirection = Direction.RIGHT;

    private static IPageWithoutHeader lastDisplayedPage = null;

    /**
     * True if every page transition should have an spinner page.
     */
    private static boolean showSpinnerBetweenTransitions = false;

    private static boolean isSpinnerStarted = false;

    /**
     * do not allow a transition if another one is already running. *
     */
    private static boolean transitionIsRunning = false;

    private static boolean allTransitionsDisabled = false;

    private final IDispatcher dispatcher;

    private final EventBus eventBus;

    private boolean transitionsFromMainViewDisabled = false;

    private boolean isVisible = false;

    private IEvent leaveToEvent;

    /**
     * Default constructor.
     *
     * @param disp der dispatcher.
     */
    public AbstractMobilePresenter(IDispatcher disp) {
        this.eventBus = null;
        this.dispatcher = disp;
    }

    /**
     * Dispatcher mit evnent Bus.
     *
     * @param eventBus der event bus
     */
    public AbstractMobilePresenter(EventBus eventBus) {
        this.dispatcher = null;
        this.eventBus = eventBus;
    }

    /**
     * Liefert die richtung der naechsten Transition. Dieses ist normalerweise recht. Falls ein Back-Navigatiosnevent
     * vorgemerkt ist ist es ausnahmsweise rechts.
     *
     * @return die Richtung des naechsten ueberganges.
     */
    public static Direction getNextTransitionDirection() {
        if (!nextTransitionDirection.equals(Direction.RIGHT)) {
            Direction toReturn = nextTransitionDirection;
            nextTransitionDirection = Direction.RIGHT;
            return toReturn;
        }
        return nextTransitionDirection;
    }

    /**
     * Enabled or disabled the spinner between transitions.
     *
     * @param showSpinnerBetweenTransitions true if the spinner should be shown between transitions.
     */
    public static void setShowSpinnerBetweenTransitions(boolean showSpinnerBetweenTransitions) {
        AbstractMobilePresenter.showSpinnerBetweenTransitions = showSpinnerBetweenTransitions;
    }

    /**
     * Return value of transitionsFromMainViewDisabled (see {@link #setAllTransitionsDisabled(boolean)} ).
     *
     * @return value
     */
    public static boolean isAllTransitionsDisabled() {
        return allTransitionsDisabled;
    }

    /**
     * If set to {code}true{code}, no transition effect is performed when page switch originates from ANY view
     * (i.e., in {@link #gotoPage(IPage, IPageWithoutHeader)
     *
     * @param allTransitionsDisabled setting of allTransitionsDisabled
     */
    public static void setAllTransitionsDisabled(boolean allTransitionsDisabled) {
        AbstractMobilePresenter.allTransitionsDisabled = allTransitionsDisabled;
    }

    @Override
    public IDispatcher getDispatcher() {
        return dispatcher;
    }

    @Override
    public LeavePageResponse leavePageRequest() {
        return LeavePageResponse.YES_LEAVE_PAGE;
    }

    /**
     * Liefert rue wenn der vom Presenter kontollierte View sichtbar ist.
     *
     * @return true wenn sichtbar.
     */
    @Override
    public boolean isOwningViewVisible() {
        return isVisible;
    }

    /**
     * Generic method to switch to another page. Shortcut for {code}gotoPage(originator, targetView, true, null, null){code}.
     *
     * @param originator the view which the event to switch the page originated from
     * @param targetView view to switch to (usually the view of the current presenter)
     */
    @Override
    public void gotoPage(final IPage originator, final IPageWithoutHeader targetView) {
        //Otherwise we get an exception
        if (targetView != null && targetView.equals(originator)) {
            return;
        }
        if (transitionsFromMainViewDisabled) {
            gotoPage(originator, targetView, !(originator instanceof IMainView), null, null);
        } else {
            gotoPage(originator, targetView, true, null, null);
        }
    }

    /**
     * Generic method to switch to another page.
     *
     * @param originator          the view which the event to switch the page originated from
     * @param targetView          view to switch to (usually the view of the current presenter)
     * @param automaticTransition whether the transition effect should be determined automatically
     *                            (parameters transition, transitionDirection are ignored)
     * @param transition          transition effect ({code}null{code} for no transition effect)
     * @param transitionDirection transition direction
     */
    @Override
    public void gotoPage(final IPage originator, final IPageWithoutHeader targetView, boolean automaticTransition,
            Transition transition, Direction transitionDirection) {
        //Otherwise we get an exception
        if (targetView != null && targetView.equals(originator)) {
            return;
        }
        isVisible = true;
        PresenterController.get().setActivePresenter(this, targetView);
        //if a page is shown but next transisition event is used with wrong originator
        if (lastDisplayedPage != null && lastDisplayedPage.getView() != originator) {
            lastDisplayedPage.getView().beforeLeaving();
        }
        lastDisplayedPage = targetView;
        setRootContentArea(originator, targetView);
        if (globalContentArea != null) {
            targetView.getView().setParent(globalContentArea);
            originator.setParent(globalContentArea);
        }

        // keine transition wenn Main-Wiew
        if (!transitionIsRunning && originator != null) {
            transitionIsRunning = true;
            //Callback for transition end
            final ITransitionCompletedCallback transitionCompletedCallback = new ITransitionCompletedCallback() {
                @Override
                public void isCompleted() {
                    LOGGER.info("Transition to Page (" + targetView.getView().getName() + ") completed");
                    stopSpinnerIfRunning();
                    transitionIsRunning = false;
                }
            };

            if (allTransitionsDisabled) {
                if (originator instanceof SimplePage) {
                    ((SimplePage) originator).goTo(targetView.getView(), null, null, transitionCompletedCallback);
                } else {
                    //Fallback if originator is not simple page
                    originator.goTo(targetView.getView(), null, transitionCompletedCallback);
                }
            } else if (automaticTransition) {
                originator.goTo(targetView.getView(), getNextTransitionDirection(), transitionCompletedCallback);
            } else if (originator instanceof SimplePage) {
                ((SimplePage) originator).goTo(targetView.getView(), transition, transitionDirection,
                        transitionCompletedCallback);
            } else {
                originator.goTo(targetView.getView(), transitionDirection, transitionCompletedCallback);
            }
        } else if (!transitionIsRunning) {
            // es gibt keinen originator
            Scheduler.get().scheduleDeferred(new ScheduledCommand() {
                @Override
                public void execute() {
                    SimplePage.load(targetView.getView());
                    stopSpinnerIfRunning();
                    transitionIsRunning = false;
                }
            });
            transitionIsRunning = true;
        } else {
            LOGGER.info("Cannot perform transition to page (" + targetView.getView().getName()
                    + "), because other transition is still running.");
        }

    }

    /**
     * Generische trasitionsmethode fuer den Seitenwechsel.
     *
     * @param targetView die view des aktuellen Presenters (Ziel) - als Content area des Ziels wird Root gesetzt.
     */
    @Override
    public void gotoPageWithRootAsContentArea(final IPageWithoutHeader targetView) {
        PresenterController.get().setActivePresenter(this, targetView);
        Scheduler.get().scheduleDeferred(new ScheduledCommand() {
            @Override
            public void execute() {
                SimplePage.load(targetView.getView());
                transitionIsRunning = false;
            }
        });
        transitionIsRunning = true;

    }

    /**
     * Generische trasitionsmethode fuer den Seitenwechsel.
     *
     * @param newRootContentArea new root content area. All new sceens will be places as chidlern of this area.
     * @param startupContent     view which should be deplayed as content of the new root panel
     */
    public void setRootContentArea(final IPage newRootContentArea, final IPageWithoutHeader startupContent) {
        // falls das Ursprungs-Panel das Main-View ist die content area wechseln
        if (globalContentArea == null && newRootContentArea != null && newRootContentArea instanceof IMainView) {
            globalContentArea = ((IMainView) newRootContentArea).toolbarPannel().getContentArea();
            globalContentArea.add(startupContent.getView().asComposite());
        }
    }

    public void fireMobileEvent(MobileEvent eventToFire, final IPage originatorPage) {
        if (eventBus != null) {
            eventToFire.setOriginatorPage(originatorPage);
            eventBus.fireEvent(eventToFire);
        }
    }

    /**
     * Will create a click hanlder, which will automatically crerate a loading indicator when clicked (if loadimng indicator is globally enabled)
     *
     * @param toWrap the callback to wrap
     * @return the wrapped click handler.
     */
    public ClickHandler createLoadingIndicatorClickHandler(final ClickHandler toWrap) {
        final ClickHandler result = new ClickHandler() {
            @Override
            public void onClick(final ClickEvent event) {
                if (!isSpinnerStarted) {
                    startPopup(new ISpinnerStarted() {
                        @Override
                        public void spinnerStarted() {
                            toWrap.onClick(event);
                            stopSpinnerIfRunning();
                        }
                    });
                }

            }
        };

        return result;
    }

    /**
     * Returns true if this screen is active.
     *
     * @return true if this screen is active.
     */
    public boolean isScreenActive() {
        if (PresenterController.get().getActivePresenter() != null) {
            return PresenterController.get().getActivePresenter().equals(this);
        }
        return false;
    }

    /**
     * Return value of transitionsFromMainViewDisabled (see {@link #setTransitionsFromMainViewDisabled(boolean)).
     *
     * @return value
     */
    public boolean isTransitionsFromMainViewDisabled() {
        return transitionsFromMainViewDisabled;
    }

    /**
     * If set to {code}true{code}, no transition effect is performed when page switch originates from main view (i.e., in {@link #gotoPage(IPage, IPageWithoutHeader) the
     * originator is an instance of {@link IMainView}). I.E., if the main view is a tab panel, direct navigation via the tab header does not
     * lead to a transition effect.
     *
     * @param transitionsFromMainViewDisabled setting of transitionsFromMainViewDisabled
     */
    public void setTransitionsFromMainViewDisabled(boolean transitionsFromMainViewDisabled) {
        this.transitionsFromMainViewDisabled = transitionsFromMainViewDisabled;
    }

    public IEvent getLeaveToEvent() {
        return leaveToEvent;
    }

    public void setLeaveToEvent(IEvent leaveToEvent) {
        this.leaveToEvent = leaveToEvent;
    }

    /**
     * Created and adds a click handler for the back button. When the back button will be clicked the passed event will
     * be fired (which should display the new page). ClickHandler does not show a spinner.
     *
     * @param myView      currently displayed view.
     * @param eventToFire the event to fire after the back button will be clicked.
     * @return the created internal click handler (e.g. to fire the event manually)
     */
    protected ClickHandler addBackButtonNavigationHandlers(final IPageWithHeader myView, final IEvent eventToFire) {
        return addBackButtonNavigationHandlers(myView, eventToFire, false);
    }

    /**
     * Created and adds a click handler for the back button. When the back button will be clicked the passed event will
     * be fired (which should display the new page). Optional showing of a spinner.
     *
     * @param myView      currently displayed view.
     * @param eventToFire the event to fire after the back button will be clicked.
     * @param showSpinner Flag if spinner should be shown
     * @return the created internal click handler (e.g. to fire the event manually)
     */
    protected ClickHandler addBackButtonNavigationHandlers(final IPageWithHeader myView, final IEvent eventToFire,
            boolean showSpinner) {
        ClickHandler backClickHandler = null;
        if (myView.getHeader() != null) {
            if (showSpinner) {
                backClickHandler = new ClickHandler() {
                    @Override
                    public void onClick(ClickEvent clickEvent) {
                        leaveToEvent = eventToFire;
                        if (!isSpinnerStarted) {
                            startPopup(new ISpinnerStarted() {
                                @Override
                                public void spinnerStarted() {
                                    PresenterController.get().beforePageLeave(new AsyncCallbackSuccess<Boolean>() {

                                        @Override
                                        public void onSuccess(Boolean result) {
                                            if (result) {
                                                if (!transitionIsRunning) {
                                                    beforeBack();
                                                    LOGGER.info("History Back Event >" + eventToFire.eventName());
                                                    fireBackEvent(eventToFire, myView);
                                                } else {
                                                    LOGGER.info(
                                                            "History Back Event > NO Action because a transition is in Progress");
                                                }
                                            }
                                        }
                                    });
                                    stopSpinnerIfRunning();
                                }
                            });
                        }
                    }
                };
            } else {
                backClickHandler = new ClickHandler() {
                    @Override
                    public void onClick(ClickEvent event) {
                        leaveToEvent = eventToFire;
                        PresenterController.get().beforePageLeave(new AsyncCallbackSuccess<Boolean>() {

                            @Override
                            public void onSuccess(Boolean result) {
                                if (result) {
                                    if (!transitionIsRunning) {
                                        beforeBack();
                                        LOGGER.info("History Back Event >" + eventToFire.eventName());
                                        fireBackEvent(eventToFire, myView);
                                    } else {
                                        LOGGER.info(
                                                "History Back Event > NO Action because a transition is in Progress");
                                    }
                                }
                            }
                        });

                    }
                };
            }
            myView.getHeader().setLeftButtonClickHandler(backClickHandler);
        }
        return backClickHandler;
    }

    /**
     * Created and adds a click handler for the back button. When the back button will be clicked the passed callback
     * will be executed.
     *
     * @param myView   currently displayed view.
     * @param callback the callback executed after the back button will be clicked.
     * @return the created internal click handler (e.g. to fire the event manually)
     */
    protected ClickHandler addBackButtonNavigationHandlers(final IPageWithHeader myView,
            final IBackCallback callback) {
        if (myView.getHeader() != null) {
            ClickHandler backClickHandler = new ClickHandler() {

                @Override
                public void onClick(final ClickEvent event) {
                    leaveToEvent = null;
                    PresenterController.get().beforePageLeave(new AsyncCallbackSuccess<Boolean>() {
                        @Override
                        public void onSuccess(Boolean result) {
                            if (result) {
                                if (!transitionIsRunning) {
                                    beforeBack();
                                    if (showSpinnerBetweenTransitions) {
                                        startPopup(new ISpinnerStarted() {
                                            @Override
                                            public void spinnerStarted() {
                                                callback.onBack(event);
                                            }
                                        });
                                    } else {
                                        callback.onBack(event);
                                    }
                                } else {
                                    LOGGER.info(
                                            "History Back Event > NO Action because a transition is in Progress");
                                }
                            }
                        }
                    });

                }
            };

            myView.getHeader().setLeftButtonClickHandler(backClickHandler);
            return backClickHandler;
        }

        return null;

    }

    /**
     * Will be called, before back action is executed.
     */
    protected void beforeBack() {
        isVisible = false;
        nextTransitionDirection = Direction.LEFT;
    }

    /**
     * Fires the back event, defined for the back button
     *
     * @param eventToFire the event to fire
     * @param myView      my view.
     */
    private void fireBackEvent(final IEvent eventToFire, final IPageWithHeader myView) {
        if (showSpinnerBetweenTransitions) {
            startPopup(new ISpinnerStarted() {
                @Override
                public void spinnerStarted() {
                    fireEvent(eventToFire, myView.getView());
                }
            });
        } else {
            fireEvent(eventToFire, myView.getView());
        }
    }

    private void fireEvent(final IEvent eventToFire, final IPage page) {
        if (this.dispatcher != null) {
            dispatcher.fireMobileEvent(eventToFire, page);
        } else if (eventBus != null && eventToFire instanceof MobileEvent) {
            fireMobileEvent((MobileEvent) eventToFire, page);
        }
    }

    /**
     * Starts a propgress bar.
     *
     * @param spinnerStarted spinner is started
     * @return the loading indicator.
     */
    private LoadingIndicatorPopup startPopup(final ISpinnerStarted spinnerStarted) {
        spinner.showCentered(true);
        final Timer timer = new Timer() {

            @Override
            public void run() {
                isSpinnerStarted = true;
                spinnerStarted.spinnerStarted();
            }
        };
        timer.schedule(50);
        return spinner;
    }

    /**
     * Will hide a loading indicator popup.
     */
    private void stopSpinnerIfRunning() {
        if (isSpinnerStarted) {
            isSpinnerStarted = false;
            spinner.setVisible(false);
        }
    }

    /**
     * Returns {code}true{code} if a transition is currently running (in this case, goToPage() has no effect).
     *
     * @return {code}true{code} if a transition is currently running
     */
    protected static boolean getTransitionIsRunning() {
        return transitionIsRunning;
    }

    /**
     * Callback, when the back button is clicked.
     */
    public static interface IBackCallback {
        void onBack(ClickEvent clickEvent);
    }
}