package org.fdroid.fdroid.compat; import android.content.res.Configuration; import android.support.v4.app.FragmentTransaction; import android.support.v4.view.ViewPager; import android.support.v7.app.ActionBar; import android.view.View; import android.view.ViewGroup; import android.widget.Spinner; import org.fdroid.fdroid.FDroid; import java.util.ArrayList; import java.util.List; public class TabManager { public static final int INDEX_AVAILABLE = 0; public static final int INDEX_INSTALLED = 1; public static final int INDEX_CAN_UPDATE = 2; public static final int INDEX_COUNT = 3; private final ViewPager pager; private final FDroid parent; private final ActionBar actionBar; private Spinner actionBarSpinner; // Used to make sure we only search for the action bar spinner once // in each orientation. private boolean dirtyConfig = true; public TabManager(FDroid parent, ViewPager pager) { actionBar = parent.getSupportActionBar(); this.parent = parent; this.pager = pager; } private CharSequence getLabel(int index) { return pager.getAdapter().getPageTitle(index); } private void removeNotification(int id) { parent.removeNotification(id); } public void createTabs() { actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); for (int i = 0; i < pager.getAdapter().getCount(); i++) { CharSequence label = pager.getAdapter().getPageTitle(i); actionBar.addTab( actionBar.newTab() .setText(label) .setTabListener(new ActionBar.TabListener() { @Override public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) { int pos = tab.getPosition(); pager.setCurrentItem(pos); if (pos == INDEX_CAN_UPDATE) { removeNotification(1); } } @Override public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) { } @Override public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) { } }) ); } } public void selectTab(int index) { actionBar.setSelectedNavigationItem(index); Spinner actionBarSpinner = getActionBarSpinner(); if (actionBarSpinner != null) { actionBarSpinner.setSelection(index); } if (index == INDEX_CAN_UPDATE) { removeNotification(1); } } public void refreshTabLabel(int index) { CharSequence text = getLabel(index); actionBar.getTabAt(index).setText(text); } public void onConfigurationChanged(Configuration newConfig) { dirtyConfig = true; } /** * Traversing the view hierarchy is a non-trivial task, and takes between 0 and 3 * milliseconds on my SGS i9000 (Android 4.2). * As such, we lazily try to identify the spinner, and only search once per * orientation change. Once we've found it, we stop looking. */ private Spinner getActionBarSpinner() { if (actionBarSpinner == null && dirtyConfig) { dirtyConfig = false; actionBarSpinner = findActionBarSpinner(); } return actionBarSpinner; } /** * Dodgey hack to fix issue 231, based on the solution at * http://stackoverflow.com/a/13353493 * Turns out that there is a bug in Android where the Spinner in the action * bar (which represents the tabs if there is not enough space) is not * updated when we call setSelectedNavigationItem(), and they don't expose * the spinner via the API. So we go on a merry hunt for all spinners in * our view, and find the first one with an id of -1. * * This is because the view hierarchy dictates that the action bar comes * before everything below it when traversing children, and also our spinner * on the first view (for the app categories) has an id, whereas the * actionbar one doesn't. If that changes in future releases of android, * then we will need to update the findListNavigationSpinner() method. */ private Spinner findActionBarSpinner() { View rootView = parent.findViewById(android.R.id.content).getRootView(); List<Spinner> spinners = traverseViewChildren((ViewGroup) rootView); return findListNavigationSpinner(spinners); } private Spinner findListNavigationSpinner(List<Spinner> spinners) { Spinner spinner = null; if (spinners.size() > 0) { Spinner first = spinners.get(0); if (first.getId() == -1) { spinner = first; } } return spinner; } private List<Spinner> traverseViewChildren(ViewGroup parent) { List<Spinner> spinners = new ArrayList<>(); for (int i = 0; i < parent.getChildCount(); i++) { View child = parent.getChildAt(i); if (child instanceof Spinner) { spinners.add((Spinner) child); } else if (child instanceof ViewGroup) { spinners.addAll(traverseViewChildren((ViewGroup) child)); } } return spinners; } }