content: package com.airbnb.android.react.navigation;
  
  import android.os.Bundle;
  import android.support.annotation.NonNull;
  import android.support.annotation.Nullable;
  import android.support.design.widget.BottomNavigationView;
  import android.support.v4.app.Fragment;
  import android.support.v4.util.ArrayMap;
  import android.util.Log;
  import android.view.Menu;
  import android.view.MenuItem;
  import android.view.View;
  import android.view.ViewGroup;
  import android.view.ViewTreeObserver;
  
  import com.airbnb.android.R;
  import com.facebook.react.bridge.ReadableMap;
  
  import java.util.Map;
  import java.util.Stack;
  
  public class ReactNativeTabActivity extends ReactAwareActivity
          implements ScreenCoordinatorComponent, BottomNavigationView.OnNavigationItemSelectedListener {
    private static final String TAG = ReactNativeTabActivity.class.getSimpleName();
  
    private ViewGroup.OnHierarchyChangeListener reactViewChangeListener = new ViewGroup.OnHierarchyChangeListener() {
      @Override
      public void onChildViewAdded(View parent, View child) {
        Log.d(TAG, "onChildViewAdded");
        if (child instanceof ViewGroup) {
          Log.d(TAG, "onChildViewAdded: adding child listener");
          // onChildViewAdded is a shallow listener, so we want to recursively listen
          // to all children that are ViewGroups as well. For a tab scene, the view
          // hierarchy should not be very deep, so this seems okay to me. We should be
          // careful though.
          ((ViewGroup)child).setOnHierarchyChangeListener(reactViewChangeListener);
        }
        debouncedRefreshTabs();
      }
  
      @Override
      public void onChildViewRemoved(View parent, View child) {
        Log.d(TAG, "onChildViewRemoved");
        // TODO(lmr): is there any reason we would need to clean up the onHierarchyChangeListener here?
        debouncedRefreshTabs();
      }
    };
  
    private TabCoordinator tabCoordinator;
    private ReactBottomNavigation bottomNavigationView;
    private ViewGroup tabConfigContainer;
    private boolean tabViewsIsDirty = false;
    private Map<Integer, TabView> tabViews = new ArrayMap<>();
    private ReadableMap prevTabBarConfig = ConversionUtil.EMPTY_MAP;
    private ReadableMap renderedTabBarConfig = ConversionUtil.EMPTY_MAP;
  
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_tab_2);
      bottomNavigationView = (ReactBottomNavigation) findViewById(R.id.bottom_navigation);
      bottomNavigationView.setOnNavigationItemSelectedListener(this);
      tabConfigContainer = (ViewGroup) findViewById(R.id.tab_config_container);
      tabConfigContainer.setOnHierarchyChangeListener(reactViewChangeListener);
      ScreenCoordinatorLayout container = (ScreenCoordinatorLayout) findViewById(R.id.content);
      tabCoordinator = new TabCoordinator(this, container, savedInstanceState);
  
      ReactNativeFragment tabConfigFragment = ReactNativeFragment.newInstance("TabScreen", null);
      getSupportFragmentManager().beginTransaction()
              .add(R.id.tab_config_container, tabConfigFragment)
              .commitNow();
    }
  
    @Override
    public ScreenCoordinator getScreenCoordinator() {
      return tabCoordinator.getCurrentScreenCoordinator();
    }
  
    @Override
    public void onBackPressed() {
      if (!tabCoordinator.onBackPressed()) {
        super.onBackPressed();
      }
    }
  
    private void debouncedRefreshTabs() {
      if (tabViewsIsDirty) {
        return;
      }
      tabViewsIsDirty = true;
      tabConfigContainer.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
          tabViewsIsDirty = false;
          tabConfigContainer.getViewTreeObserver().removeOnPreDrawListener(this);
          refreshTabs();
          return true;
        }
      });
    }
  
    private void refreshTabs() {
      Log.d(TAG, "refreshTabs");
      traverseTabs();
      notifyTabsHaveChanged();
    }
  
    private void traverseTabs() {
      Stack<ViewGroup> stack = new Stack<>();
      stack.push(tabConfigContainer);
  
      prevTabBarConfig = renderedTabBarConfig;
      renderedTabBarConfig = ConversionUtil.EMPTY_MAP;
      tabViews = new ArrayMap<>();
  
      while (!stack.empty()) {
        ViewGroup view = stack.pop();
        int childCount = view.getChildCount();
        for (int i = 0; i < childCount; ++i) {
          View child = view.getChildAt(i);
  
          if (child instanceof TabView) {
            tabViews.put(child.getId(), (TabView) child);
          } else if (child instanceof TabBarView) {
            TabBarView tabBarView = (TabBarView) child;
            renderedTabBarConfig = ConversionUtil.combine(renderedTabBarConfig, tabBarView.getConfig());
            stack.push(tabBarView);
          } else if (child instanceof ViewGroup) {
            stack.push((ViewGroup) child);
          }
        }
      }
    }
  
    private void notifyTabsHaveChanged() {
      Log.d(TAG, "notifyTabsHaveChanged");
      Menu menu = bottomNavigationView.getMenu();
  
      getImplementation().reconcileTabBarProperties(
              bottomNavigationView,
              menu,
              prevTabBarConfig,
              renderedTabBarConfig
      );
  
      menu.clear();
      bottomNavigationView.clearIconHolders();
  
      int index = 0;
      for (TabView tab : tabViews.values()) {
        getImplementation().makeTabItem(
                bottomNavigationView,
                menu,
                index,
                tab.getId(),
                tab.getRenderedConfig()
        );
        index++;
      }
  
      if (tabViews.size() > 0) {
        TabView view = tabViews.values().iterator().next();
        tabCoordinator.showTab(view.getFragment(), view.getId());
      }
    }
  
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
      Log.d(TAG, "onNavigationItemSelected");
      TabView tab = tabViews.get(item.getItemId());
      if (tab != null) {
        Log.d(TAG, "found tab");
        Fragment fragment = tab.getFragment();
        tabCoordinator.showTab(fragment, item.getItemId());
      }
      return true;
    }
  }
  