123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- package com.reactnativenavigation.viewcontrollers;
-
- import android.app.Activity;
- import android.view.View;
- import android.view.ViewGroup;
- import android.view.ViewManager;
- import android.view.ViewTreeObserver;
-
- import com.reactnativenavigation.interfaces.ScrollEventListener;
- import com.reactnativenavigation.parse.Options;
- import com.reactnativenavigation.parse.params.Bool;
- import com.reactnativenavigation.parse.params.NullBool;
- import com.reactnativenavigation.presentation.FabPresenter;
- import com.reactnativenavigation.utils.CommandListener;
- import com.reactnativenavigation.utils.Functions.Func1;
- import com.reactnativenavigation.utils.StringUtils;
- import com.reactnativenavigation.utils.UiThread;
- import com.reactnativenavigation.utils.UiUtils;
- import com.reactnativenavigation.viewcontrollers.stack.StackController;
- import com.reactnativenavigation.viewcontrollers.viewcontrolleroverlay.ViewControllerOverlay;
- import com.reactnativenavigation.views.BehaviourAdapter;
- import com.reactnativenavigation.views.Component;
- import com.reactnativenavigation.views.Renderable;
-
- import java.util.ArrayList;
- import java.util.List;
-
- import androidx.annotation.CallSuper;
- import androidx.annotation.CheckResult;
- import androidx.annotation.NonNull;
- import androidx.annotation.Nullable;
- import androidx.annotation.VisibleForTesting;
- import androidx.coordinatorlayout.widget.CoordinatorLayout;
-
- import static com.reactnativenavigation.utils.CollectionUtils.*;
- import static com.reactnativenavigation.utils.ObjectUtils.perform;
-
- public abstract class ViewController<T extends ViewGroup> implements ViewTreeObserver.OnGlobalLayoutListener,
- ViewGroup.OnHierarchyChangeListener,
- BehaviourAdapter {
-
- private final List<Runnable> onAppearedListeners = new ArrayList<>();
- private boolean appearEventPosted;
- private boolean isFirstLayout = true;
- private Bool waitForRender = new NullBool();
-
- public interface ViewVisibilityListener {
- /**
- * @return true if the event is consumed, false otherwise
- */
- boolean onViewAppeared(View view);
-
- /**
- * @return true if the event is consumed, false otherwise
- */
- boolean onViewDisappear(View view);
- }
-
- public Options initialOptions;
- public Options options;
-
- private final Activity activity;
- private final String id;
- private YellowBoxDelegate yellowBoxDelegate;
- @Nullable protected T view;
- @Nullable private ParentController<T> parentController;
- private boolean isShown;
- private boolean isDestroyed;
- private ViewVisibilityListener viewVisibilityListener = new ViewVisibilityListenerAdapter();
- protected FabPresenter fabOptionsPresenter;
- private ViewControllerOverlay overlay;
-
- public boolean isDestroyed() {
- return isDestroyed;
- }
-
- public ViewController(Activity activity, String id, YellowBoxDelegate yellowBoxDelegate, Options initialOptions, ViewControllerOverlay overlay) {
- this.activity = activity;
- this.id = id;
- this.yellowBoxDelegate = yellowBoxDelegate;
- fabOptionsPresenter = new FabPresenter();
- this.initialOptions = initialOptions;
- this.overlay = overlay;
- options = initialOptions.copy();
- }
-
- public void setWaitForRender(Bool waitForRender) {
- this.waitForRender = waitForRender;
- }
-
- public ScrollEventListener getScrollEventListener() {
- return null;
- }
-
- public void addOnAppearedListener(Runnable onAppearedListener) {
- if (isShown) {
- onAppearedListener.run();
- } else {
- onAppearedListeners.add(onAppearedListener);
- }
- }
-
- public void removeOnAppearedListener(Runnable onAppearedListener) {
- onAppearedListeners.remove(onAppearedListener);
- }
-
- protected abstract T createView();
-
- public void setViewVisibilityListener(ViewVisibilityListener viewVisibilityListener) {
- this.viewVisibilityListener = viewVisibilityListener;
- }
-
- @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
- public void ensureViewIsCreated() {
- getView();
- }
-
- public boolean handleBack(CommandListener listener) {
- return false;
- }
-
- public void addOverlay(View v) {
- perform(view, view -> overlay.add(view, v));
- }
-
- public void removeOverlay(View view) {
- overlay.remove(view);
- }
-
- @CheckResult
- public Options resolveCurrentOptions() {
- return options;
- }
-
- @CheckResult
- public Options resolveCurrentOptions(Options defaultOptions) {
- return options.copy().withDefaultOptions(defaultOptions);
- }
-
- @CallSuper
- public void mergeOptions(Options options) {
- this.initialOptions = this.initialOptions.mergeWith(options);
- this.options = this.options.mergeWith(options);
- if (getParentController() != null) {
- this.options.clearOneTimeOptions();
- initialOptions.clearOneTimeOptions();
- }
- }
-
- @CallSuper
- public void applyOptions(Options options) {
-
- }
-
- public void setDefaultOptions(Options defaultOptions) {
-
- }
-
- public Activity getActivity() {
- return activity;
- }
-
- public void performOnView(Func1<View> task) {
- if (view != null) task.run(view);
- }
-
- public void performOnParentController(Func1<ParentController> task) {
- if (parentController != null) task.run(parentController);
- }
-
- @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
- public ParentController getParentController() {
- return parentController;
- }
-
- public ParentController requireParentController() {
- return parentController;
- }
-
- public void setParentController(@NonNull final ParentController parentController) {
- this.parentController = parentController;
- }
-
- public void performOnParentStack(Func1<StackController> task) {
- if (parentController instanceof StackController) {
- task.run((StackController) parentController);
- } else if (this instanceof StackController) {
- task.run((StackController) this);
- } else performOnParentController(parent -> parent.performOnParentStack(task));
- }
-
- public T getView() {
- if (view == null) {
- if (isDestroyed) {
- throw new RuntimeException("Tried to create view after it has already been destroyed");
- }
- view = createView();
- view.setOnHierarchyChangeListener(this);
- view.getViewTreeObserver().addOnGlobalLayoutListener(this);
- }
- return view;
- }
-
- public void detachView() {
- if (view == null || view.getParent() == null) return;
- ((ViewManager) view.getParent()).removeView(view);
- }
-
- public void attachView(ViewGroup parent, int index) {
- if (view == null) return;
- if (view.getParent() == null) parent.addView(view, index);
- }
-
- public String getId() {
- return id;
- }
-
- boolean isSameId(final String id) {
- return StringUtils.isEqual(this.id, id);
- }
-
- @Nullable
- public ViewController findController(String id) {
- return isSameId(id) ? this : null;
- }
-
- @Nullable
- public ViewController findController(View child) {
- return view == child ? this : null;
- }
-
- public boolean containsComponent(Component component) {
- return getView().equals(component);
- }
-
- public void onViewWillAppear() {
-
- }
-
- @CallSuper
- public void onViewAppeared() {
- isShown = true;
- applyOptions(options);
- performOnParentController(parentController -> {
- parentController.clearOptions();
- if (getView() instanceof Component) parentController.applyChildOptions(options, this);
- });
- if (!onAppearedListeners.isEmpty() && !appearEventPosted) {
- appearEventPosted = true;
- UiThread.post(() -> {
- forEach(onAppearedListeners, Runnable::run);
- onAppearedListeners.clear();
- });
- }
- }
-
- public void onViewWillDisappear() {
-
- }
-
- @CallSuper
- public void onViewDisappear() {
- isShown = false;
- }
-
- @CallSuper
- public void destroy() {
- if (isShown) {
- isShown = false;
- onViewDisappear();
- }
- yellowBoxDelegate.destroy();
- if (view instanceof Destroyable) {
- ((Destroyable) view).destroy();
- }
- if (view != null) {
- view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
- view.setOnHierarchyChangeListener(null);
- if (view.getParent() instanceof ViewGroup) {
- ((ViewManager) view.getParent()).removeView(view);
- }
- view = null;
- isDestroyed = true;
- }
- }
-
- @Override
- public void onGlobalLayout() {
- if (isFirstLayout) {
- onAttachToParent();
- isFirstLayout = false;
- }
- if (!isShown && isViewShown()) {
- if (!viewVisibilityListener.onViewAppeared(view)) {
- isShown = true;
- onViewAppeared();
- }
- } else if (isShown && !isViewShown()) {
- if (!viewVisibilityListener.onViewDisappear(view)) {
- isShown = false;
- onViewDisappear();
- }
- }
- }
-
- public void onAttachToParent() {
-
- }
-
- @Override
- public void onChildViewAdded(View parent, View child) {
- yellowBoxDelegate.onChildViewAdded(parent, child);
- }
-
- @Override
- public void onChildViewRemoved(View view, View view1) {
-
- }
-
- void runOnPreDraw(Func1<T> task) {
- if (!isDestroyed) UiUtils.runOnPreDrawOnce(getView(), task);
- }
-
- public abstract void sendOnNavigationButtonPressed(String buttonId);
-
- public boolean isViewShown() {
- return !isDestroyed &&
- getView().isShown() &&
- view != null &&
- isRendered();
- }
-
- public boolean isRendered() {
- return view != null && (
- waitForRender.isFalseOrUndefined() ||
- !(view instanceof Renderable) ||
- ((Renderable) view).isRendered()
- );
- }
-
- void applyOnController(ViewController controller, Func1<ViewController> task) {
- if (controller != null) task.run(controller);
- }
-
- @Override
- @CallSuper
- public boolean onMeasureChild(CoordinatorLayout parent, ViewGroup child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
- perform(findController(child), ViewController::applyTopInset);
- return false;
- }
-
- @Override
- public boolean onDependentViewChanged(CoordinatorLayout parent, ViewGroup child, View dependency) {
- return false;
- }
-
- public void applyTopInset() {
-
- }
-
- public int getTopInset() {
- return 0;
- }
-
- public void applyBottomInset() {
-
- }
-
- public int getBottomInset() {
- return perform(parentController, 0, p -> p.getBottomInset(this));
- }
- }
|