All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.swm.commons.mobile.client.presenter.AbstractMobilePresenter Maven / Gradle / Ivy

/*
 * 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() {

										@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() {

							@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() {
						@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.
	 */
	public 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.
	 */
	public 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);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy