package com.gh4a; import android.annotation.TargetApi; import android.content.res.Configuration; import android.graphics.Color; import android.os.Build; import android.os.Bundle; import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.widget.Toolbar; import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; import android.widget.EdgeEffect; import android.widget.LinearLayout; import com.gh4a.loader.LoaderCallbacks; import com.gh4a.utils.UiUtils; import com.gh4a.widget.SwipeRefreshLayout; import java.lang.reflect.Field; public abstract class BasePagerActivity extends BaseActivity implements SwipeRefreshLayout.ChildScrollDelegate, ViewPager.OnPageChangeListener { private FragmentAdapter mAdapter; private TabLayout mTabs; private ViewPager mPager; private int[][] mTabHeaderColors; private boolean mScrolling; private boolean mErrorViewVisible; private int mCurrentHeaderColor; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mAdapter = new FragmentAdapter(); setContentView(R.layout.view_pager); mCurrentHeaderColor = UiUtils.resolveColor(this, R.attr.colorPrimary); updateTabHeaderColors(); mPager = setupPager(); updateTabVisibility(); setChildScrollDelegate(this); } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); onPageMoved(0, 0); } protected void invalidateFragments() { mAdapter.notifyDataSetChanged(); updateTabVisibility(); } protected void invalidateTabs() { invalidateFragments(); updateTabHeaderColors(); if (mTabHeaderColors != null) { onPageMoved(0, 0); } else { int[] colorAttrs = getHeaderColorAttrs(); if (colorAttrs != null) { transitionHeaderToColor(colorAttrs[0], colorAttrs[1]); } else { transitionHeaderToColor(R.attr.colorPrimary, R.attr.colorPrimaryDark); } } tryUpdatePagerColor(); } protected ViewPager getPager() { return mPager; } @Override protected void setErrorViewVisibility(boolean visible) { mErrorViewVisible = visible; updateTabVisibility(); super.setErrorViewVisibility(visible); } @Override public void onRefresh() { for (int i = 0; i < mAdapter.getCount(); i++) { Fragment f = mAdapter.getExistingFragment(i); if (f instanceof LoaderCallbacks.ParentCallback) { ((LoaderCallbacks.ParentCallback) f).onRefresh(); } } super.onRefresh(); } @Override public boolean canChildScrollUp() { Fragment item = mAdapter.getCurrentFragment(); if (item != null && item instanceof SwipeRefreshLayout.ChildScrollDelegate) { return ((SwipeRefreshLayout.ChildScrollDelegate) item).canChildScrollUp(); } return false; } private void updateTabHeaderColors() { int[][] colorAttrs = getTabHeaderColorAttrs(); if (colorAttrs == null) { mTabHeaderColors = null; return; } mTabHeaderColors = new int[colorAttrs.length][2]; for (int i = 0; i < mTabHeaderColors.length; i++) { mTabHeaderColors[i][0] = UiUtils.resolveColor(this, colorAttrs[i][0]); mTabHeaderColors[i][1] = UiUtils.resolveColor(this, colorAttrs[i][1]); } } private ViewPager setupPager() { ViewPager pager = (ViewPager) findViewById(R.id.pager); pager.setAdapter(mAdapter); pager.addOnPageChangeListener(this); mTabs = (TabLayout) getLayoutInflater().inflate(R.layout.tab_bar, null); mTabs.setupWithViewPager(pager); if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); toolbar.addView(mTabs, new Toolbar.LayoutParams( Toolbar.LayoutParams.WRAP_CONTENT, Toolbar.LayoutParams.MATCH_PARENT)); } else { addHeaderView(mTabs, false); } return pager; } private void updateTabVisibility() { int count = mAdapter.getCount(); // We never have many pages, make sure to keep them all alive mPager.setOffscreenPageLimit(Math.max(1, count - 1)); mTabs.setVisibility(count > 1 && !mErrorViewVisible ? View.VISIBLE : View.GONE); setToolbarScrollable(count > 1 && getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE); LinearLayout tabStrip = (LinearLayout) mTabs.getChildAt(0); for (int i = 0; i < tabStrip.getChildCount(); i++) { View tab = tabStrip.getChildAt(i); LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) tab.getLayoutParams(); lp.width = LinearLayout.LayoutParams.WRAP_CONTENT; lp.weight = 1; tab.setLayoutParams(lp); } } @Override protected void setHeaderColor(int color, int statusBarColor) { super.setHeaderColor(color, statusBarColor); mCurrentHeaderColor = color; } @Override protected void transitionHeaderToColor(int colorAttrId, int statusBarColorAttrId) { super.transitionHeaderToColor(colorAttrId, statusBarColorAttrId); mCurrentHeaderColor = UiUtils.resolveColor(this, colorAttrId); } protected abstract int[] getTabTitleResIds(); protected abstract Fragment makeFragment(int position); protected boolean fragmentNeedsRefresh(Fragment object) { return false; } protected void onFragmentInstantiated(Fragment f, int position) { } protected void onFragmentDestroyed(Fragment f) { } /* expected format: int[tabCount][2] - 0 is header, 1 is status bar */ protected int[][] getTabHeaderColorAttrs() { return null; } /* expected format: int[2] - 0 is header, 1 is status bar */ protected int[] getHeaderColorAttrs() { return null; } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { onPageMoved(position, positionOffset); } @Override public void onPageSelected(int position) { if (!mScrolling) { onPageMoved(position, 0); } } @Override public void onPageScrollStateChanged(int state) { mScrolling = state != ViewPager.SCROLL_STATE_IDLE; if (!mScrolling) { tryUpdatePagerColor(); } } protected void onPageMoved(int position, float fraction) { if (mTabHeaderColors != null) { int nextIndex = Math.max(0, Math.min(position + 1, mTabHeaderColors.length - 1)); int headerColor = UiUtils.mixColors(mTabHeaderColors[position][0], mTabHeaderColors[nextIndex][0], fraction); int statusBarColor = UiUtils.mixColors(mTabHeaderColors[position][1], mTabHeaderColors[nextIndex][1], fraction); setHeaderColor(headerColor, statusBarColor); } } private class FragmentAdapter extends FragmentStatePagerAdapter { private final SparseArray<Fragment> mFragments = new SparseArray<>(); private Fragment mCurrentFragment; public FragmentAdapter() { super(getSupportFragmentManager()); } @Override public int getCount() { int[] titleResIds = getTabTitleResIds(); return titleResIds != null ? titleResIds.length : 0; } @Override public Fragment getItem(int position) { return makeFragment(position); } @Override public Object instantiateItem(ViewGroup container, int position) { Fragment f = (Fragment) super.instantiateItem(container, position); mFragments.put(position, f); onFragmentInstantiated(f, position); return f; } private Fragment getExistingFragment(int position) { return mFragments.get(position); } private Fragment getCurrentFragment() { return mCurrentFragment; } @Override public CharSequence getPageTitle(int position) { return getString(getTabTitleResIds()[position]); } @Override public void destroyItem(ViewGroup container, int position, Object object) { super.destroyItem(container, position, object); mFragments.remove(position); onFragmentDestroyed((Fragment) object); if (object == mCurrentFragment) { mCurrentFragment = null; } } @Override public void setPrimaryItem(ViewGroup container, int position, Object object) { mCurrentFragment = (Fragment) object; super.setPrimaryItem(container, position, object); } @Override public int getItemPosition(Object object) { if (object instanceof Fragment && fragmentNeedsRefresh((Fragment) object)) { return POSITION_NONE; } return POSITION_UNCHANGED; } } private void tryUpdatePagerColor() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { ViewPagerEdgeColorHelper helper = (ViewPagerEdgeColorHelper) mPager.getTag(R.id.EdgeColorHelper); if (helper == null) { helper = new ViewPagerEdgeColorHelper(mPager); mPager.setTag(R.id.EdgeColorHelper, helper); } helper.setColor(mCurrentHeaderColor); } } @TargetApi(21) private static class ViewPagerEdgeColorHelper { private final ViewPager mPager; private int mColor; private EdgeEffect mLeftEffect, mRightEffect; private static Field sLeftEffectField, sRightEffectField; public ViewPagerEdgeColorHelper(ViewPager pager) { mPager = pager; mColor = 0; } public void setColor(int color) { mColor = color; applyIfPossible(); } private void applyIfPossible() { if (!ensureStaticFields()) { return; } if (mLeftEffect == null || mRightEffect == null) { try { Object leftEffect = sLeftEffectField.get(mPager); Object rightEffect = sRightEffectField.get(mPager); final Field edgeField = leftEffect.getClass().getDeclaredField("mEdgeEffect"); edgeField.setAccessible(true); mLeftEffect = (EdgeEffect) edgeField.get(leftEffect); mRightEffect = (EdgeEffect) edgeField.get(rightEffect); } catch (IllegalAccessException | NoSuchFieldException e) { mLeftEffect = mRightEffect = null; } } applyColor(mLeftEffect); applyColor(mRightEffect); } private void applyColor(EdgeEffect effect) { if (effect != null) { final int alpha = Color.alpha(effect.getColor()); effect.setColor(Color.argb(alpha, Color.red(mColor), Color.green(mColor), Color.blue(mColor))); } } private boolean ensureStaticFields() { if (sLeftEffectField != null && sRightEffectField != null) { return true; } try { sLeftEffectField = ViewPager.class.getDeclaredField("mLeftEdge"); sLeftEffectField.setAccessible(true); sRightEffectField = ViewPager.class.getDeclaredField("mRightEdge"); sRightEffectField.setAccessible(true); return true; } catch (NoSuchFieldException e) { // ignored } return false; } } }