Package com.googlecode.mgwt.mvp.client

Source Code of com.googlecode.mgwt.mvp.client.AnimatingActivityManager$ProtectedDisplay

/*
* Copyright 2010 Daniel Kurka
*
* 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.googlecode.mgwt.mvp.client;

import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Set;

import com.google.gwt.activity.shared.AbstractActivity;
import com.google.gwt.activity.shared.Activity;
import com.google.gwt.activity.shared.ActivityManager;
import com.google.gwt.activity.shared.ActivityMapper;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.event.shared.ResettableEventBus;
import com.google.gwt.event.shared.UmbrellaException;
import com.google.gwt.place.shared.Place;
import com.google.gwt.place.shared.PlaceChangeEvent;
import com.google.gwt.place.shared.PlaceChangeRequestEvent;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.web.bindery.event.shared.EventBus;

import com.googlecode.mgwt.ui.client.widget.animation.AnimatableDisplay;
import com.googlecode.mgwt.ui.client.widget.animation.Animation;
import com.googlecode.mgwt.ui.client.widget.animation.AnimationEndCallback;

/**
* This is a fork of @link {@link ActivityManager} that has the same features,
* but also adds animations to the lifecycle of Activities.
*
* It can be used as a replacement for {@link ActivityManager}, but requires an
* instance of {@link AnimationMapper} to work properly
*
* @author Daniel Kurka
*/
public class AnimatingActivityManager implements PlaceChangeEvent.Handler, PlaceChangeRequestEvent.Handler {

  /**
   * Wraps our real display to prevent an Activity from taking it over if it
   * is not the currentActivity.
   */
  private class ProtectedDisplay implements AcceptsOneWidget {
    private final Activity activity;

    public ProtectedDisplay(Activity activity) {
      this.activity = activity;
    }

    public void setWidget(IsWidget view) {
      if (this.activity == AnimatingActivityManager.this.currentActivity) {
        startingNext = false;
        showWidget(view, currentIsFirst);
      } else {
        if (this.activity == AnimatingActivityManager.this.lastActivity) {
          showWidget(view, currentIsFirst);
        }
      }
    }
  }

  private static final Activity NULL_ACTIVITY = new AbstractActivity() {
    @Override
    public void start(AcceptsOneWidget panel, com.google.gwt.event.shared.EventBus eventBus) {
      panel.setWidget(null);
    }
  };

  private final ActivityMapper mapper;

  private final EventBus eventBus;
  private final ResettableEventBus stopperedEventBus;

  private Activity currentActivity = NULL_ACTIVITY;
  private Place currentPlace;
  private boolean currentIsFirst = false;

  private Activity lastActivity = NULL_ACTIVITY;

  private AnimatableDisplay display;

  private boolean startingNext = false;

  private HandlerRegistration handlerRegistration;

  private final AnimationMapper animationMapper;
  private boolean listeningForAnimationEnd = false;
  private boolean ignorePlaceChange = false;
  private boolean fireAnimationEvents;
  private LinkedList<PlaceChangeEvent> placeChangeStack = new LinkedList<PlaceChangeEvent>();

  /**
   * Create an ActivityManager. Next call {@link #setDisplay}.
   *
   * @param mapper finds the {@link Activity} for a given
   *            {@link com.google.gwt.place.shared.Place}
   * @param eventBus source of {@link PlaceChangeEvent} and
   *            {@link PlaceChangeRequestEvent} events.
   * @param animationMapper a
   *            {@link com.googlecode.mgwt.mvp.client.AnimationMapper} object.
   */
  public AnimatingActivityManager(ActivityMapper mapper, AnimationMapper animationMapper, EventBus eventBus) {
    this.mapper = mapper;
    this.animationMapper = animationMapper;
    this.eventBus = eventBus;
    this.stopperedEventBus = new ResettableEventBus(eventBus);
  }

  /**
   * {@inheritDoc}
   *
   * Deactivate the current activity, find the next one from our
   * ActivityMapper, and start it.
   * <p>
   * The current activity's widget will be hidden immediately, which can cause
   * flicker if the next activity provides its widget asynchronously. That can
   * be minimized by decent caching. Perenially slow activities might mitigate
   * this by providing a widget immediately, with some kind of "loading"
   * treatment.
   *
   * @see com.google.gwt.place.shared.PlaceChangeEvent.Handler#onPlaceChange(PlaceChangeEvent)
   */
  public void onPlaceChange(PlaceChangeEvent event) {
    if (ignorePlaceChange) {
      //remember the place change event to be executed after the current one is done
      placeChangeStack.add(event);
      return;
    }

    Activity nextActivity = getNextActivity(event);
    Animation animation = getAnimation(event);

    Throwable caughtOnStop = null;
    Throwable caughtOnStart = null;

    if (nextActivity == null) {
      nextActivity = NULL_ACTIVITY;
    }

    if (currentActivity.equals(nextActivity)) {
      return;
    }

    if (startingNext) {
      // The place changed again before the new current activity showed
      // its
      // widget
      currentActivity.onCancel();
      currentActivity = NULL_ACTIVITY;
      startingNext = false;
    } else {

      /*
       * Kill off the activity's handlers, so it doesn't have to worry
       * about them accidentally firing as a side effect of its tear down
       */
      stopperedEventBus.removeHandlers();

      try {
        currentActivity.onStop();
      } catch (Throwable t) {
        caughtOnStop = t;
      } finally {
        /*
         * And kill them off again in case it was naughty and added new
         * ones during onstop
         */
        stopperedEventBus.removeHandlers();
      }
    }

    currentActivity = nextActivity;
    currentPlace = event.getNewPlace();

    if (animation != null) {
      currentIsFirst = !currentIsFirst;
    }

    startingNext = true;

    /*
     * Now start the thing. Wrap the actual display with a per-call instance
     * that protects the display from canceled or stopped activities, and
     * which maintains our startingNext state.
     */
    try {
      currentActivity.start(new ProtectedDisplay(currentActivity), stopperedEventBus);
    } catch (Throwable t) {
      caughtOnStart = t;
    }

    if (caughtOnStart != null || caughtOnStop != null) {
      Set<Throwable> causes = new LinkedHashSet<Throwable>();
      if (caughtOnStop != null) {
        causes.add(caughtOnStop);
      }
      if (caughtOnStart != null) {
        causes.add(caughtOnStart);
      }

      throw new UmbrellaException(causes);
    }

    // animate
    animate(animation);
  }

  public void setFireAnimationEvents(boolean fireAnimationEvents) {
    this.fireAnimationEvents = fireAnimationEvents;
  }

  public boolean isFireAnimationEvents() {
    return fireAnimationEvents;
  }

  private void onAnimationEnd() {
    if (listeningForAnimationEnd) {

      if (fireAnimationEvents) {
        eventBus.fireEvent(new MGWTAnimationEndEvent());
      }

      if (!placeChangeStack.isEmpty()) {
        //execute deffered so that animation end will fire!
        Scheduler.get().scheduleDeferred(new ScheduledCommand() {

          @Override
          public void execute() {
            ignorePlaceChange = false;
            PlaceChangeEvent poll = placeChangeStack.poll();
            onPlaceChange(poll);
          }
        });

      } else {
        ignorePlaceChange = false;
      }
    }
  }

  private void animate(Animation animation) {

    listeningForAnimationEnd = true;
    ignorePlaceChange = true;

    if (fireAnimationEvents) {
      eventBus.fireEvent(new MGWTAnimationStartEvent());
    }

    if (animation == null) {
      AnimatingActivityManager.this.onAnimationEnd();
      return;
    }

    display.animate(animation, currentIsFirst, new AnimationEndCallback() {

      @Override
      public void onAnimationEnd() {
        AnimatingActivityManager.this.onAnimationEnd();

      }
    });
  }

  private Animation getAnimation(PlaceChangeEvent event) {
    Place newPlace = event.getNewPlace();
    return animationMapper.getAnimation(currentPlace, newPlace);
  }

  public void onPlaceChangeRequest(PlaceChangeRequestEvent event) {
    if (!currentActivity.equals(NULL_ACTIVITY)) {
      event.setWarning(currentActivity.mayStop());
    }
  }

  /**
   * Sets the display for the receiver, and has the side effect of starting or
   * stopping its monitoring the event bus for place change events.
   * <p>
   * If you are disposing of an ActivityManager, it is important to call
   * setDisplay(null) to get it to deregister from the event bus, so that it
   * can be garbage collected.
   *
   * @param display an instance of AcceptsOneWidget
   */
  public void setDisplay(AnimatableDisplay display) {
    boolean wasActive = (null != this.display);
    boolean willBeActive = (null != display);
    this.display = display;

    if (wasActive != willBeActive) {
      updateHandlers(willBeActive);
    }

  }

  private Activity getNextActivity(PlaceChangeEvent event) {
    if (display == null) {
      /*
       * Display may have been nulled during PlaceChangeEvent dispatch.
       * Don't bother the mapper, just return a null to ensure we shut
       * down the current activity
       */
      return null;
    }
    return mapper.getActivity(event.getNewPlace());
  }

  private void showWidget(IsWidget view, boolean first) {
    if (display != null) {
      if (first) {
        display.setFirstWidget(view);
      } else {
        display.setSecondWidget(view);
      }
    }
  }

  private void updateHandlers(boolean activate) {
    if (activate) {
      final com.google.web.bindery.event.shared.HandlerRegistration placeReg = eventBus.addHandler(PlaceChangeEvent.TYPE, this);
      final com.google.web.bindery.event.shared.HandlerRegistration placeRequestReg = eventBus.addHandler(PlaceChangeRequestEvent.TYPE, this);

      this.handlerRegistration = new HandlerRegistration() {
        public void removeHandler() {
          placeReg.removeHandler();
          placeRequestReg.removeHandler();
        }
      };
    } else {
      if (handlerRegistration != null) {
        handlerRegistration.removeHandler();
        handlerRegistration = null;
      }
    }
  }
}
TOP

Related Classes of com.googlecode.mgwt.mvp.client.AnimatingActivityManager$ProtectedDisplay

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.