package com.lyft.scoop; import android.content.Context; import android.util.AttributeSet; import android.view.DragEvent; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.widget.FrameLayout; import com.lyft.scoop.transitions.InstantTransition; import java.util.ArrayDeque; public abstract class UiContainer extends FrameLayout implements HandleBack, TransitionListener { private ArrayDeque<RouteChange> screenSwapQueue = new ArrayDeque<>(); private boolean isTransitioning; private View active; public UiContainer(Context context) { this(context, null, 0); } public UiContainer(Context context, AttributeSet attrs) { this(context, attrs, 0); } public UiContainer(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); if (isInEditMode()) { return; } } protected ViewControllerInflater getViewControllerInflater() { return new ViewControllerInflater(); } protected LayoutInflater getLayoutInflater() { return new LayoutInflater(); } protected ScreenScooper getScreenScooper() { return new ScreenScooper(new ScreenScoopFactory()); } public boolean onBack() { return childCanGoBack(); } public void goTo(RouteChange routeChange) { if (screenSwapQueue.isEmpty()) { screenSwapQueue.add(routeChange); swap(routeChange); } else { screenSwapQueue.add(routeChange); } } @Override public void onTransitionCompleted() { final TransitionListener transitionListener = getTransitionListener(); transitionListener.onTransitionCompleted(); isTransitioning = false; if (!screenSwapQueue.isEmpty()) { screenSwapQueue.pop(); if (!screenSwapQueue.isEmpty()) { swap(screenSwapQueue.peek()); } } } @Override public boolean dispatchTouchEvent(MotionEvent event) { if (isTransitioning) { return true; } else { return super.dispatchTouchEvent(event); } } @Override public boolean dispatchDragEvent(DragEvent event) { if (isTransitioning) { return true; } else { return super.dispatchDragEvent(event); } } @Override public boolean dispatchKeyEvent(KeyEvent event) { if (isTransitioning) { return true; } else { return super.dispatchKeyEvent(event); } } @Override public boolean dispatchKeyEventPreIme(KeyEvent event) { if (isTransitioning) { return true; } else { return super.dispatchKeyEventPreIme(event); } } @Override public boolean dispatchKeyShortcutEvent(KeyEvent event) { if (isTransitioning) { return true; } else { return super.dispatchKeyShortcutEvent(event); } } @Override public boolean dispatchTrackballEvent(MotionEvent event) { if (isTransitioning) { return true; } else { return super.dispatchTrackballEvent(event); } } private void swap(RouteChange routeChange) { final Scoop currentScoop = getActiveScoop(routeChange); final ScreenSwap screenSwap = routeChange.toScreenSwap(); Screen previousScreen = screenSwap.previous; Screen nextScreen = screenSwap.next; final View prevView = active; ViewController nextViewController = null; if (active != null && previousScreen != null) { previousScreen.saveViewState(active); } if (nextScreen == null) { active = null; } else if (nextScreen.getController() != null) { nextViewController = getViewControllerInflater().inflateViewController(currentScoop, nextScreen.getController(), this); active = inflateViewFromController(nextViewController, currentScoop); nextScreen.restoreViewState(active); } else { active = inflateLayout(nextScreen, currentScoop); nextScreen.restoreViewState(active); } isTransitioning = true; ScreenTransition transition; if (screenSwap.direction == TransitionDirection.ENTER) { transition = getTransition(nextViewController, screenSwap.direction); } else { transition = getTransition(ViewController.fromView(prevView), screenSwap.direction); } transition.transition(this, prevView, active, this); } private View inflateViewFromController(ViewController viewController, final Scoop scoop) { View view = scoop.inflate(viewController.layoutId(), this, false); ViewControllerInflater.bindViewControllerToView(view, viewController); return view; } private View inflateLayout(Screen nextScreen, final Scoop scoop) { return getLayoutInflater().inflateView(scoop, nextScreen, this); } static ScreenTransition getTransition(ViewController viewController, TransitionDirection transitionDirection) { if (viewController == null) { return new InstantTransition(); } if (transitionDirection == TransitionDirection.ENTER) { return viewController.enterTransition(); } else { return viewController.exitTransition(); } } private boolean childCanGoBack() { if (active instanceof HandleBack) { return handleBack(active); } ViewController viewController = ViewController.fromView(active); if (viewController != null) { if (viewController instanceof HandleBack) { return handleBack(viewController); } } return false; } private boolean handleBack(final Object object) { HandleBack handleBack = (HandleBack) object; return handleBack.onBack(); } private Scoop getActiveScoop(final RouteChange routeChange) { Scoop rootScoop = Scoop.fromView(this); Scoop currentScreenScoop = Scoop.fromView(active); return getScreenScooper().create(rootScoop, currentScreenScoop, routeChange.fromPath, routeChange.toPath); } private TransitionListener getTransitionListener() { ViewController controller = ViewController.fromView(active); TransitionListener transitionListener = new NoOpTransitionListener(); if (controller instanceof TransitionListener) { transitionListener = (TransitionListener) controller; } return transitionListener; } }