// Path: stage/src/main/java/com/hippo/stage/Curtain.java
// public abstract class Curtain {
// 
//   /**
//    * Completes this {@code Curtain} right now.
//    * The {@link OnCompleteListener} passed in
//    * {@link #execute(ViewGroup, SceneInfo, List, OnCompleteListener)} before,
//    * must be called in the method.
//    */
//   protected abstract void completeImmediately();
// 
//   /**
//    * Executes this {@code Curtain} with {@code upper} and {@code lower}.
//    * {@code listener} must be called in this method or after the animation done.
//    */
//   protected abstract void execute(@NonNull ViewGroup container, @NonNull SceneInfo upper,
//       @NonNull List<SceneInfo> lower, @NonNull OnCompleteListener listener);
// 
//   /**
//    * A listener for being notified when the {@link Curtain} is complete.
//    */
//   public interface OnCompleteListener {
// 
//     /**
//      * Called when the {@code Curtain} is complete.
//      */
//     void onComplete();
//   }
// }
// 
// Path: stage/src/main/java/com/hippo/stage/SceneInfo.java
// public final class SceneInfo {
// 
//   @IntDef({NONE, NEWLY_ATTACHED, WILL_BE_DETACHED})
//   @Retention(RetentionPolicy.SOURCE)
//   public @interface ViewState {}
// 
//   /**
//    * The view is still attached.
//    */
//   public static final int NONE = 0;
//   /**
//    * The view is newly attached.
//    */
//   public static final int NEWLY_ATTACHED = 1;
//   /**
//    * The view will be detached.
//    */
//   public static final int WILL_BE_DETACHED = 2;
// 
//   /**
//    * The described {@link Scene}.
//    */
//   @NonNull
//   public final Scene scene;
//   /**
//    * The view of the {@code Scene}.
//    */
//   @NonNull
//   public final View view;
//   /**
//    * One of {@link #NONE}, {@link #NEWLY_ATTACHED} or {@link #WILL_BE_DETACHED}.
//    */
//   @ViewState
//   public final int viewState;
// 
//   final boolean isStarted;
// 
//   private SceneInfo(Builder builder) {
//     scene = builder.scene;
//     view = builder.view;
//     viewState = builder.viewState;
//     isStarted = builder.isStarted;
//   }
// 
//   static class Builder {
// 
//     private static final boolean DEBUG = BuildConfig.DEBUG;
// 
//     private Scene scene;
//     private View view;
//     @ViewState
//     private int viewState;
//     private boolean isStarted;
// 
//     public Builder scene(@NonNull Scene scene) {
//       this.scene = scene;
//       this.view = scene.getView();
// 
//       if (DEBUG) {
//         if (scene.getView() == null) {
//           throw new IllegalStateException("View of the scene is null: " + scene);
//         }
//       }
// 
//       return this;
//     }
// 
//     public Builder viewState(@ViewState int viewState) {
//       this.viewState = viewState;
//       return this;
//     }
// 
//     public Builder isStarted(boolean isStarted) {
//       this.isStarted = isStarted;
//       return this;
//     }
// 
//     public SceneInfo build() {
//       return new SceneInfo(this);
//     }
//   }
// }


import com.hippo.stage.SceneInfo;
import java.util.List;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.TimeInterpolator;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import com.hippo.stage.Curtain;

    if (listener != null) {
      listener.onComplete();
      listener = null;
    }
  }

  @Nullable
  private View getFirstNonLaidOutView(@NonNull SceneInfo upper, @NonNull List<SceneInfo> lower) {
    if (!isLaidOut(upper.view)) {
      return upper.view;
    }
    for (SceneInfo info : lower) {
      if (!isLaidOut(info.view)) {
        return info.view;
      }
    }
    return null;
  }

  @Override
  protected void execute(@NonNull final ViewGroup container, @NonNull final SceneInfo upper,
      @NonNull final List<SceneInfo> lower, @NonNull final OnCompleteListener listener) {
    this.listener = listener;
    view = getFirstNonLaidOutView(upper, lower);
    if (view != null) {
      onPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
          if (view != null && onPreDrawListener != null) {
// Path: stage/src/main/java/com/hippo/stage/Curtain.java
// public abstract class Curtain {
// 
//   /**
//    * Completes this {@code Curtain} right now.
//    * The {@link OnCompleteListener} passed in
//    * {@link #execute(ViewGroup, SceneInfo, List, OnCompleteListener)} before,
//    * must be called in the method.
//    */
//   protected abstract void completeImmediately();
// 
//   /**
//    * Executes this {@code Curtain} with {@code upper} and {@code lower}.
//    * {@code listener} must be called in this method or after the animation done.
//    */
//   protected abstract void execute(@NonNull ViewGroup container, @NonNull SceneInfo upper,
//       @NonNull List<SceneInfo> lower, @NonNull OnCompleteListener listener);
// 
//   /**
//    * A listener for being notified when the {@link Curtain} is complete.
//    */
//   public interface OnCompleteListener {
// 
//     /**
//      * Called when the {@code Curtain} is complete.
//      */
//     void onComplete();
//   }
// }
// 
// Path: stage/src/main/java/com/hippo/stage/SceneInfo.java
// public final class SceneInfo {
// 
//   @IntDef({NONE, NEWLY_ATTACHED, WILL_BE_DETACHED})
//   @Retention(RetentionPolicy.SOURCE)
//   public @interface ViewState {}
// 
//   /**
//    * The view is still attached.
//    */
//   public static final int NONE = 0;
//   /**
//    * The view is newly attached.
//    */
//   public static final int NEWLY_ATTACHED = 1;
//   /**
//    * The view will be detached.
//    */
//   public static final int WILL_BE_DETACHED = 2;
// 
//   /**
//    * The described {@link Scene}.
//    */
//   @NonNull
//   public final Scene scene;
//   /**
//    * The view of the {@code Scene}.
//    */
//   @NonNull
//   public final View view;
//   /**
//    * One of {@link #NONE}, {@link #NEWLY_ATTACHED} or {@link #WILL_BE_DETACHED}.
//    */
//   @ViewState
//   public final int viewState;
// 
//   final boolean isStarted;
// 
//   private SceneInfo(Builder builder) {
//     scene = builder.scene;
//     view = builder.view;
//     viewState = builder.viewState;
//     isStarted = builder.isStarted;
//   }
// 
//   static class Builder {
// 
//     private static final boolean DEBUG = BuildConfig.DEBUG;
// 
//     private Scene scene;
//     private View view;
//     @ViewState
//     private int viewState;
//     private boolean isStarted;
// 
//     public Builder scene(@NonNull Scene scene) {
//       this.scene = scene;
//       this.view = scene.getView();
// 
//       if (DEBUG) {
//         if (scene.getView() == null) {
//           throw new IllegalStateException("View of the scene is null: " + scene);
//         }
//       }
// 
//       return this;
//     }
// 
//     public Builder viewState(@ViewState int viewState) {
//       this.viewState = viewState;
//       return this;
//     }
// 
//     public Builder isStarted(boolean isStarted) {
//       this.isStarted = isStarted;
//       return this;
//     }
// 
//     public SceneInfo build() {
//       return new SceneInfo(this);
//     }
//   }
// }


// Path: stage/src/main/java/com/hippo/stage/curtain/AnimatorCurtain.java
import com.hippo.stage.SceneInfo;
import java.util.List;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.TimeInterpolator;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import com.hippo.stage.Curtain;


    if (listener != null) {
      listener.onComplete();
      listener = null;
    }
  }

  @Nullable
  private View getFirstNonLaidOutView(@NonNull SceneInfo upper, @NonNull List<SceneInfo> lower) {
    if (!isLaidOut(upper.view)) {
      return upper.view;
    }
    for (SceneInfo info : lower) {
      if (!isLaidOut(info.view)) {
        return info.view;
      }
    }
    return null;
  }

  @Override
  protected void execute(@NonNull final ViewGroup container, @NonNull final SceneInfo upper,
      @NonNull final List<SceneInfo> lower, @NonNull final OnCompleteListener listener) {
    this.listener = listener;
    view = getFirstNonLaidOutView(upper, lower);
    if (view != null) {
      onPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
          if (view != null && onPreDrawListener != null) {
            view.getViewTreeObserver().removeOnPreDrawListener(onPreDrawListener);