diff --git a/AndroidManifest.xml b/AndroidManifest.xml index af749b73..5351b2c6 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -42,7 +42,6 @@ THE SOFTWARE. diff --git a/orig/folder.svgz b/orig/folder.svgz index 44315d19..5622bf26 100644 Binary files a/orig/folder.svgz and b/orig/folder.svgz differ diff --git a/res/color/tab_text_selector.xml b/res/color/tab_text_selector.xml new file mode 100644 index 00000000..438fd917 --- /dev/null +++ b/res/color/tab_text_selector.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/res/drawable-hdpi/folder.png b/res/drawable-hdpi/folder.png index 438f50c6..77d7e869 100644 Binary files a/res/drawable-hdpi/folder.png and b/res/drawable-hdpi/folder.png differ diff --git a/res/drawable-hdpi/vpi__tab_selected_focused_holo.9.png b/res/drawable-hdpi/vpi__tab_selected_focused_holo.9.png deleted file mode 100644 index a6966544..00000000 Binary files a/res/drawable-hdpi/vpi__tab_selected_focused_holo.9.png and /dev/null differ diff --git a/res/drawable-hdpi/vpi__tab_selected_holo.9.png b/res/drawable-hdpi/vpi__tab_selected_holo.9.png deleted file mode 100644 index 6c516941..00000000 Binary files a/res/drawable-hdpi/vpi__tab_selected_holo.9.png and /dev/null differ diff --git a/res/drawable-hdpi/vpi__tab_selected_pressed_holo.9.png b/res/drawable-hdpi/vpi__tab_selected_pressed_holo.9.png deleted file mode 100644 index 40ab0164..00000000 Binary files a/res/drawable-hdpi/vpi__tab_selected_pressed_holo.9.png and /dev/null differ diff --git a/res/drawable-hdpi/vpi__tab_unselected_focused_holo.9.png b/res/drawable-hdpi/vpi__tab_unselected_focused_holo.9.png deleted file mode 100644 index 6dcd91d6..00000000 Binary files a/res/drawable-hdpi/vpi__tab_unselected_focused_holo.9.png and /dev/null differ diff --git a/res/drawable-hdpi/vpi__tab_unselected_holo.9.png b/res/drawable-hdpi/vpi__tab_unselected_holo.9.png deleted file mode 100644 index 88973973..00000000 Binary files a/res/drawable-hdpi/vpi__tab_unselected_holo.9.png and /dev/null differ diff --git a/res/drawable-hdpi/vpi__tab_unselected_pressed_holo.9.png b/res/drawable-hdpi/vpi__tab_unselected_pressed_holo.9.png deleted file mode 100644 index 96bb26ba..00000000 Binary files a/res/drawable-hdpi/vpi__tab_unselected_pressed_holo.9.png and /dev/null differ diff --git a/res/drawable-mdpi/folder.png b/res/drawable-mdpi/folder.png index 58bff4d0..137b88cf 100644 Binary files a/res/drawable-mdpi/folder.png and b/res/drawable-mdpi/folder.png differ diff --git a/res/drawable-mdpi/vpi__tab_selected_focused_holo.9.png b/res/drawable-mdpi/vpi__tab_selected_focused_holo.9.png deleted file mode 100644 index e2bf86ef..00000000 Binary files a/res/drawable-mdpi/vpi__tab_selected_focused_holo.9.png and /dev/null differ diff --git a/res/drawable-mdpi/vpi__tab_selected_holo.9.png b/res/drawable-mdpi/vpi__tab_selected_holo.9.png deleted file mode 100644 index dc663ebe..00000000 Binary files a/res/drawable-mdpi/vpi__tab_selected_holo.9.png and /dev/null differ diff --git a/res/drawable-mdpi/vpi__tab_selected_pressed_holo.9.png b/res/drawable-mdpi/vpi__tab_selected_pressed_holo.9.png deleted file mode 100644 index 49b2feb6..00000000 Binary files a/res/drawable-mdpi/vpi__tab_selected_pressed_holo.9.png and /dev/null differ diff --git a/res/drawable-mdpi/vpi__tab_unselected_focused_holo.9.png b/res/drawable-mdpi/vpi__tab_unselected_focused_holo.9.png deleted file mode 100644 index 3a6b2946..00000000 Binary files a/res/drawable-mdpi/vpi__tab_unselected_focused_holo.9.png and /dev/null differ diff --git a/res/drawable-mdpi/vpi__tab_unselected_holo.9.png b/res/drawable-mdpi/vpi__tab_unselected_holo.9.png deleted file mode 100644 index 2a380724..00000000 Binary files a/res/drawable-mdpi/vpi__tab_unselected_holo.9.png and /dev/null differ diff --git a/res/drawable-mdpi/vpi__tab_unselected_pressed_holo.9.png b/res/drawable-mdpi/vpi__tab_unselected_pressed_holo.9.png deleted file mode 100644 index c9714f7e..00000000 Binary files a/res/drawable-mdpi/vpi__tab_unselected_pressed_holo.9.png and /dev/null differ diff --git a/res/drawable-xhdpi/folder.png b/res/drawable-xhdpi/folder.png index 3bc35402..669b0ea5 100644 Binary files a/res/drawable-xhdpi/folder.png and b/res/drawable-xhdpi/folder.png differ diff --git a/res/drawable-xhdpi/vpi__tab_selected_focused_holo.9.png b/res/drawable-xhdpi/vpi__tab_selected_focused_holo.9.png deleted file mode 100644 index 793d6b57..00000000 Binary files a/res/drawable-xhdpi/vpi__tab_selected_focused_holo.9.png and /dev/null differ diff --git a/res/drawable-xhdpi/vpi__tab_selected_holo.9.png b/res/drawable-xhdpi/vpi__tab_selected_holo.9.png deleted file mode 100644 index 95a825e9..00000000 Binary files a/res/drawable-xhdpi/vpi__tab_selected_holo.9.png and /dev/null differ diff --git a/res/drawable-xhdpi/vpi__tab_selected_pressed_holo.9.png b/res/drawable-xhdpi/vpi__tab_selected_pressed_holo.9.png deleted file mode 100644 index 6491ff29..00000000 Binary files a/res/drawable-xhdpi/vpi__tab_selected_pressed_holo.9.png and /dev/null differ diff --git a/res/drawable-xhdpi/vpi__tab_unselected_focused_holo.9.png b/res/drawable-xhdpi/vpi__tab_unselected_focused_holo.9.png deleted file mode 100644 index a81410bf..00000000 Binary files a/res/drawable-xhdpi/vpi__tab_unselected_focused_holo.9.png and /dev/null differ diff --git a/res/drawable-xhdpi/vpi__tab_unselected_holo.9.png b/res/drawable-xhdpi/vpi__tab_unselected_holo.9.png deleted file mode 100644 index 08f2348c..00000000 Binary files a/res/drawable-xhdpi/vpi__tab_unselected_holo.9.png and /dev/null differ diff --git a/res/drawable-xhdpi/vpi__tab_unselected_pressed_holo.9.png b/res/drawable-xhdpi/vpi__tab_unselected_pressed_holo.9.png deleted file mode 100644 index 0ccd88b8..00000000 Binary files a/res/drawable-xhdpi/vpi__tab_unselected_pressed_holo.9.png and /dev/null differ diff --git a/res/drawable-xxhdpi/folder.png b/res/drawable-xxhdpi/folder.png index 75ca48b8..38676410 100644 Binary files a/res/drawable-xxhdpi/folder.png and b/res/drawable-xxhdpi/folder.png differ diff --git a/res/drawable/tab_focus.9.png b/res/drawable/tab_focus.9.png deleted file mode 100644 index 59c4d19f..00000000 Binary files a/res/drawable/tab_focus.9.png and /dev/null differ diff --git a/res/drawable/tab_indicator.xml b/res/drawable/tab_indicator.xml deleted file mode 100644 index af6b524b..00000000 --- a/res/drawable/tab_indicator.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/res/drawable/tab_selected.9.png b/res/drawable/tab_selected.9.png deleted file mode 100644 index 136e3cb9..00000000 Binary files a/res/drawable/tab_selected.9.png and /dev/null differ diff --git a/res/drawable/tab_unselected.9.png b/res/drawable/tab_unselected.9.png deleted file mode 100644 index 890fd94a..00000000 Binary files a/res/drawable/tab_unselected.9.png and /dev/null differ diff --git a/res/layout/library_content.xml b/res/layout/library_content.xml index 3862eba3..104852e9 100644 --- a/res/layout/library_content.xml +++ b/res/layout/library_content.xml @@ -27,13 +27,12 @@ THE SOFTWARE. android:layout_width="fill_parent" android:layout_height="fill_parent"> - + + @color/vanillaAccent + + @color/vanillaPrimary + @android:color/primary_text_dark + #ffeeeeee #f000 diff --git a/res/values-v21/theme.xml b/res/values-v21/theme.xml index f39a79d8..4240a4f2 100644 --- a/res/values-v21/theme.xml +++ b/res/values-v21/theme.xml @@ -40,8 +40,6 @@ Copyright (C) 2015 Adrian Ulrich @@ -53,17 +51,6 @@ Copyright (C) 2015 Adrian Ulrich - - - - @@ -87,8 +74,6 @@ Copyright (C) 2015 Adrian Ulrich diff --git a/res/values/colors.xml b/res/values/colors.xml new file mode 100644 index 00000000..bb127083 --- /dev/null +++ b/res/values/colors.xml @@ -0,0 +1,11 @@ + + + + @android:color/holo_blue_dark + #fff5f5f5 + + @android:color/holo_blue_dark + + #ff1e1e1e + @android:color/holo_blue_dark + diff --git a/res/values/theme.xml b/res/values/theme.xml index 3e952ffc..b02f6eb1 100644 --- a/res/values/theme.xml +++ b/res/values/theme.xml @@ -21,13 +21,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --> - - @android:color/holo_blue_dark - #fff5f5f5 - - - @android:color/holo_blue_dark - - diff --git a/src/android/support/iosched/tabs/SlidingTabLayout.java b/src/android/support/iosched/tabs/SlidingTabLayout.java new file mode 100644 index 00000000..86b0997e --- /dev/null +++ b/src/android/support/iosched/tabs/SlidingTabLayout.java @@ -0,0 +1,322 @@ +// imported from https://raw.githubusercontent.com/google/iosched/2531cbdbe27e5795eb78bf47d27e8c1be494aad4/android/src/main/java/com/google/samples/apps/iosched/ui/widget/SlidingTabLayout.java +/* + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.support.iosched.tabs; + +import android.content.Context; +import android.graphics.Typeface; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.util.SparseArray; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.HorizontalScrollView; +import android.widget.LinearLayout; +import android.widget.TextView; + +/** + * To be used with ViewPager to provide a tab indicator component which give constant feedback as to + * the user's scroll progress. + *

+ * To use the component, simply add it to your view hierarchy. Then in your + * {@link android.app.Activity} or {@link android.support.v4.app.Fragment} call + * {@link #setViewPager(ViewPager)} providing it the ViewPager this layout is being used for. + *

+ * The colors can be customized in two ways. The first and simplest is to provide an array of colors + * via {@link #setSelectedIndicatorColors(int...)}. The + * alternative is via the {@link TabColorizer} interface which provides you complete control over + * which color is used for any individual position. + *

+ * The views used as tabs can be customized by calling {@link #setCustomTabView(int, int)}, + * providing the layout ID of your custom layout. + */ +public class SlidingTabLayout extends HorizontalScrollView { + /** + * Allows complete control over the colors drawn in the tab layout. Set with + * {@link #setCustomTabColorizer(TabColorizer)}. + */ + public interface TabColorizer { + + /** + * @return return the color of the indicator used when {@code position} is selected. + */ + int getIndicatorColor(int position); + + } + + private static final int TITLE_OFFSET_DIPS = 24; + private static final int TAB_VIEW_PADDING_DIPS = 16; + private static final int TAB_VIEW_TEXT_SIZE_SP = 12; + + private int mTitleOffset; + + private int mTabViewLayoutId; + private int mTabViewTextViewId; + private boolean mDistributeEvenly; + + private ViewPager mViewPager; + private SparseArray mContentDescriptions = new SparseArray(); + private ViewPager.OnPageChangeListener mViewPagerPageChangeListener; + + private final SlidingTabStrip mTabStrip; + + public SlidingTabLayout(Context context) { + this(context, null); + } + + public SlidingTabLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + // Disable the Scroll Bar + setHorizontalScrollBarEnabled(false); + // Make sure that the Tab Strips fills this View + setFillViewport(true); + + mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density); + + mTabStrip = new SlidingTabStrip(context); + addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + } + + /** + * Set the custom {@link TabColorizer} to be used. + * + * If you only require simple custmisation then you can use + * {@link #setSelectedIndicatorColors(int...)} to achieve + * similar effects. + */ + public void setCustomTabColorizer(TabColorizer tabColorizer) { + mTabStrip.setCustomTabColorizer(tabColorizer); + } + + public void setDistributeEvenly(boolean distributeEvenly) { + mDistributeEvenly = distributeEvenly; + } + + /** + * Sets the colors to be used for indicating the selected tab. These colors are treated as a + * circular array. Providing one color will mean that all tabs are indicated with the same color. + */ + public void setSelectedIndicatorColors(int... colors) { + mTabStrip.setSelectedIndicatorColors(colors); + } + + /** + * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are + * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so + * that the layout can update it's scroll position correctly. + * + * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener) + */ + public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) { + mViewPagerPageChangeListener = listener; + } + + /** + * Set the custom layout to be inflated for the tab views. + * + * @param layoutResId Layout id to be inflated + * @param textViewId id of the {@link TextView} in the inflated view + */ + public void setCustomTabView(int layoutResId, int textViewId) { + mTabViewLayoutId = layoutResId; + mTabViewTextViewId = textViewId; + } + + /** + * Sets the associated view pager. Note that the assumption here is that the pager content + * (number of tabs and tab titles) does not change after this call has been made. + */ + public void setViewPager(ViewPager viewPager) { + mTabStrip.removeAllViews(); + + mViewPager = viewPager; + if (viewPager != null) { + viewPager.setOnPageChangeListener(new InternalViewPagerListener()); + populateTabStrip(); + } + } + + /** + * Create a default view to be used for tabs. This is called if a custom tab view is not set via + * {@link #setCustomTabView(int, int)}. + */ + protected TextView createDefaultTabView(Context context) { + TextView textView = new TextView(context); + textView.setGravity(Gravity.CENTER); + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, TAB_VIEW_TEXT_SIZE_SP); + textView.setTypeface(Typeface.DEFAULT_BOLD); + textView.setLayoutParams(new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + + TypedValue outValue = new TypedValue(); + getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, + outValue, true); + textView.setBackgroundResource(outValue.resourceId); + textView.setAllCaps(true); + + int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density); + textView.setPadding(padding, padding, padding, padding); + + return textView; + } + + private void populateTabStrip() { + final PagerAdapter adapter = mViewPager.getAdapter(); + final View.OnClickListener tabClickListener = new TabClickListener(); + + for (int i = 0; i < adapter.getCount(); i++) { + View tabView = null; + TextView tabTitleView = null; + + if (mTabViewLayoutId != 0) { + // If there is a custom tab view layout id set, try and inflate it + tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip, + false); + tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId); + } + + if (tabView == null) { + tabView = createDefaultTabView(getContext()); + } + + if (tabTitleView == null && TextView.class.isInstance(tabView)) { + tabTitleView = (TextView) tabView; + } + + if (mDistributeEvenly) { + LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) tabView.getLayoutParams(); + lp.width = 0; + lp.weight = 1; + } + + tabTitleView.setText(adapter.getPageTitle(i)); + tabView.setOnClickListener(tabClickListener); + String desc = mContentDescriptions.get(i, null); + if (desc != null) { + tabView.setContentDescription(desc); + } + + mTabStrip.addView(tabView); + if (i == mViewPager.getCurrentItem()) { + tabView.setSelected(true); + } + } + } + + public void setContentDescription(int i, String desc) { + mContentDescriptions.put(i, desc); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (mViewPager != null) { + scrollToTab(mViewPager.getCurrentItem(), 0); + } + } + + private void scrollToTab(int tabIndex, int positionOffset) { + final int tabStripChildCount = mTabStrip.getChildCount(); + if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) { + return; + } + + View selectedChild = mTabStrip.getChildAt(tabIndex); + if (selectedChild != null) { + int targetScrollX = selectedChild.getLeft() + positionOffset; + + if (tabIndex > 0 || positionOffset > 0) { + // If we're not at the first child and are mid-scroll, make sure we obey the offset + targetScrollX -= mTitleOffset; + } + + scrollTo(targetScrollX, 0); + } + } + + private class InternalViewPagerListener implements ViewPager.OnPageChangeListener { + private int mScrollState; + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + int tabStripChildCount = mTabStrip.getChildCount(); + if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) { + return; + } + + mTabStrip.onViewPagerPageChanged(position, positionOffset); + + View selectedTitle = mTabStrip.getChildAt(position); + int extraOffset = (selectedTitle != null) + ? (int) (positionOffset * selectedTitle.getWidth()) + : 0; + scrollToTab(position, extraOffset); + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageScrolled(position, positionOffset, + positionOffsetPixels); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + mScrollState = state; + + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageScrollStateChanged(state); + } + } + + @Override + public void onPageSelected(int position) { + if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { + mTabStrip.onViewPagerPageChanged(position, 0f); + scrollToTab(position, 0); + } + for (int i = 0; i < mTabStrip.getChildCount(); i++) { + mTabStrip.getChildAt(i).setSelected(position == i); + } + if (mViewPagerPageChangeListener != null) { + mViewPagerPageChangeListener.onPageSelected(position); + } + } + + } + + private class TabClickListener implements View.OnClickListener { + @Override + public void onClick(View v) { + for (int i = 0; i < mTabStrip.getChildCount(); i++) { + if (v == mTabStrip.getChildAt(i)) { + mViewPager.setCurrentItem(i); + return; + } + } + } + } + +} diff --git a/src/android/support/iosched/tabs/SlidingTabStrip.java b/src/android/support/iosched/tabs/SlidingTabStrip.java new file mode 100644 index 00000000..7d709925 --- /dev/null +++ b/src/android/support/iosched/tabs/SlidingTabStrip.java @@ -0,0 +1,166 @@ +// imported from https://raw.githubusercontent.com/google/iosched/2531cbdbe27e5795eb78bf47d27e8c1be494aad4/android/src/main/java/com/google/samples/apps/iosched/ui/widget/SlidingTabStrip.java +/* + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.support.iosched.tabs; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; +import android.widget.LinearLayout; + +class SlidingTabStrip extends LinearLayout { + + private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 0; + private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26; + private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 3; + private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5; + + private final int mBottomBorderThickness; + private final Paint mBottomBorderPaint; + + private final int mSelectedIndicatorThickness; + private final Paint mSelectedIndicatorPaint; + + private int mSelectedPosition; + private float mSelectionOffset; + + private SlidingTabLayout.TabColorizer mCustomTabColorizer; + private final SimpleTabColorizer mDefaultTabColorizer; + + SlidingTabStrip(Context context) { + this(context, null); + } + + SlidingTabStrip(Context context, AttributeSet attrs) { + super(context, attrs); + setWillNotDraw(false); + + final float density = getResources().getDisplayMetrics().density; + + TypedValue outValue = new TypedValue(); + context.getTheme().resolveAttribute(android.R.attr.colorForeground, outValue, true); + final int themeForegroundColor = outValue.data; + + int defaultBottomBorderColor = setColorAlpha(themeForegroundColor, + DEFAULT_BOTTOM_BORDER_COLOR_ALPHA); + + mDefaultTabColorizer = new SimpleTabColorizer(); + mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR); + + mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density); + mBottomBorderPaint = new Paint(); + mBottomBorderPaint.setColor(defaultBottomBorderColor); + + mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density); + mSelectedIndicatorPaint = new Paint(); + } + + void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) { + mCustomTabColorizer = customTabColorizer; + invalidate(); + } + + void setSelectedIndicatorColors(int... colors) { + // Make sure that the custom colorizer is removed + mCustomTabColorizer = null; + mDefaultTabColorizer.setIndicatorColors(colors); + invalidate(); + } + + void onViewPagerPageChanged(int position, float positionOffset) { + mSelectedPosition = position; + mSelectionOffset = positionOffset; + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + final int height = getHeight(); + final int childCount = getChildCount(); + final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null + ? mCustomTabColorizer + : mDefaultTabColorizer; + + // Thick colored underline below the current selection + if (childCount > 0) { + View selectedTitle = getChildAt(mSelectedPosition); + int left = selectedTitle.getLeft(); + int right = selectedTitle.getRight(); + int color = tabColorizer.getIndicatorColor(mSelectedPosition); + + if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) { + int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1); + if (color != nextColor) { + color = blendColors(nextColor, color, mSelectionOffset); + } + + // Draw the selection partway between the tabs + View nextTitle = getChildAt(mSelectedPosition + 1); + left = (int) (mSelectionOffset * nextTitle.getLeft() + + (1.0f - mSelectionOffset) * left); + right = (int) (mSelectionOffset * nextTitle.getRight() + + (1.0f - mSelectionOffset) * right); + } + + mSelectedIndicatorPaint.setColor(color); + + canvas.drawRect(left, height - mSelectedIndicatorThickness, right, + height, mSelectedIndicatorPaint); + } + + // Thin underline along the entire bottom edge + canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint); + } + + /** + * Set the alpha value of the {@code color} to be the given {@code alpha} value. + */ + private static int setColorAlpha(int color, byte alpha) { + return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color)); + } + + /** + * Blend {@code color1} and {@code color2} using the given ratio. + * + * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend, + * 0.0 will return {@code color2}. + */ + private static int blendColors(int color1, int color2, float ratio) { + final float inverseRation = 1f - ratio; + float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation); + float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation); + float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation); + return Color.rgb((int) r, (int) g, (int) b); + } + + private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer { + private int[] mIndicatorColors; + + @Override + public final int getIndicatorColor(int position) { + return mIndicatorColors[position % mIndicatorColors.length]; + } + + void setIndicatorColors(int... colors) { + mIndicatorColors = colors; + } + } +} diff --git a/src/android/support/iosched/tabs/VanillaTabLayout.java b/src/android/support/iosched/tabs/VanillaTabLayout.java new file mode 100644 index 00000000..9bd6625d --- /dev/null +++ b/src/android/support/iosched/tabs/VanillaTabLayout.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2015-2016 Adrian Ulrich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package android.support.iosched.tabs; + +import android.app.ActionBar; +import android.content.Context; +import android.util.AttributeSet; +import android.widget.TextView; + +import android.os.Build; + +/** + * Simple wrapper for SlidingTabLayout which takes + * care of setting sane per-platform defaults + */ +public class VanillaTabLayout extends SlidingTabLayout { + + public VanillaTabLayout(Context context) { + this(context, null); + } + + public VanillaTabLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public VanillaTabLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + setSelectedIndicatorColors(context.getResources().getColor(ch.blinkenlights.android.vanilla.R.color.tabs_active_indicator)); + setDistributeEvenly(true); + } + + /** + * Overrides the default text color + */ + @Override + protected TextView createDefaultTabView(Context context) { + TextView view = super.createDefaultTabView(context); + view.setTextColor(getResources().getColorStateList(ch.blinkenlights.android.vanilla.R.color.tab_text_selector)); + view.setTextSize(14); + return view; + } + + /** + * Borrow elevation of given action bar + * + * @param ab The active action bar + */ + public void inheritElevation(ActionBar ab) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) + return; // noop on earlier releases + + float elevation = ab.getElevation(); + ab.setElevation(0.0f); + setElevation(elevation); + } + + + +} diff --git a/src/ch/blinkenlights/android/vanilla/CompatHoneycomb.java b/src/ch/blinkenlights/android/vanilla/CompatHoneycomb.java deleted file mode 100644 index 9abb5570..00000000 --- a/src/ch/blinkenlights/android/vanilla/CompatHoneycomb.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2012 Christopher Eby - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package ch.blinkenlights.android.vanilla; - -import android.annotation.TargetApi; -import android.app.ActionBar; -import android.app.ActionBar.Tab; -import android.app.Activity; -import android.app.FragmentTransaction; -import android.net.Uri; -import android.provider.MediaStore; -import android.view.KeyEvent; -import android.view.MenuItem; -import android.view.View; -import android.widget.ListView; - -/** - * Framework methods only in Honeycomb or above go here. - */ -@TargetApi(11) -public class CompatHoneycomb { - /** - * Add ActionBar tabs for LibraryActivity. - * - * @param activity The activity to add to. - */ - public static void addActionBarTabs(final LibraryActivity activity) - { - ActionBar.TabListener listener = new ActionBar.TabListener() { - private final LibraryActivity mActivity = activity; - - @Override - public void onTabReselected(Tab tab, FragmentTransaction ft) - { - } - - @Override - public void onTabSelected(Tab tab, FragmentTransaction ft) - { - mActivity.mViewPager.setCurrentItem(tab.getPosition()); - } - - @Override - public void onTabUnselected(Tab tab, FragmentTransaction ft) - { - } - }; - - ActionBar ab = activity.getActionBar(); - ab.removeAllTabs(); - int[] order = activity.mPagerAdapter.mTabOrder; - int[] titles = LibraryPagerAdapter.TITLES; - for (int i = 0, n = activity.mPagerAdapter.getCount(); i != n; ++i) { - ab.addTab(ab.newTab().setText(titles[order[i]]).setTabListener(listener)); - } - ab.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); - } - - /** - * Select the ActionBar tab at the given position. - * - * @param activity The activity that owns the ActionBar. - * @param position The tab's position. - */ - public static void selectTab(Activity activity, int position) - { - ActionBar ab = activity.getActionBar(); - if (position < ab.getTabCount()) { - ab.selectTab(ab.getTabAt(position)); - } - } - -} diff --git a/src/ch/blinkenlights/android/vanilla/FileSystemAdapter.java b/src/ch/blinkenlights/android/vanilla/FileSystemAdapter.java index 6548d554..36557a4a 100644 --- a/src/ch/blinkenlights/android/vanilla/FileSystemAdapter.java +++ b/src/ch/blinkenlights/android/vanilla/FileSystemAdapter.java @@ -201,7 +201,10 @@ public class FileSystemAdapter holder = new ViewHolder(); holder.text = (TextView)view.findViewById(R.id.text); holder.divider = (View)view.findViewById(R.id.divider); + holder.cover = (LazyCoverView)view.findViewById(R.id.cover); holder.arrow = (ImageView)view.findViewById(R.id.arrow); + + holder.cover.setImageDrawable(mFolderIcon); holder.arrow.setOnClickListener(this); view.setTag(holder); } else { @@ -215,7 +218,7 @@ public class FileSystemAdapter holder.text.setText(file.getName()); holder.arrow.setVisibility(isDirectory ? View.VISIBLE : View.GONE); holder.divider.setVisibility(isDirectory ? View.VISIBLE : View.GONE); - holder.text.setCompoundDrawablesWithIntrinsicBounds(isDirectory ? mFolderIcon : null, null, null, null); + holder.cover.setVisibility(isDirectory ? View.VISIBLE : View.GONE); return view; } diff --git a/src/ch/blinkenlights/android/vanilla/FilebrowserStartAdapter.java b/src/ch/blinkenlights/android/vanilla/FilebrowserStartAdapter.java index 72a5bfb1..e4b4aa9b 100644 --- a/src/ch/blinkenlights/android/vanilla/FilebrowserStartAdapter.java +++ b/src/ch/blinkenlights/android/vanilla/FilebrowserStartAdapter.java @@ -55,8 +55,13 @@ public class FilebrowserStartAdapter holder = new ViewHolder(); holder.text = (TextView)view.findViewById(R.id.text); holder.divider = view.findViewById(R.id.divider); + holder.cover = (LazyCoverView)view.findViewById(R.id.cover); + holder.arrow = (ImageView)view.findViewById(R.id.arrow); + + holder.cover.setImageDrawable(mFolderIcon); holder.text.setOnClickListener(this); + holder.cover.setVisibility(View.VISIBLE); view.setTag(holder); } else { view = convertView; @@ -66,9 +71,6 @@ public class FilebrowserStartAdapter String label = getItem(pos); holder.id = pos; holder.text.setText(label); - holder.divider.setVisibility(View.GONE); - holder.arrow.setVisibility(View.GONE); - holder.text.setCompoundDrawablesWithIntrinsicBounds(mFolderIcon, null, null, null); return view; } diff --git a/src/ch/blinkenlights/android/vanilla/LibraryActivity.java b/src/ch/blinkenlights/android/vanilla/LibraryActivity.java index eb685285..a4ecb57f 100644 --- a/src/ch/blinkenlights/android/vanilla/LibraryActivity.java +++ b/src/ch/blinkenlights/android/vanilla/LibraryActivity.java @@ -40,6 +40,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.provider.MediaStore; +import android.support.iosched.tabs.VanillaTabLayout; import android.support.v4.view.ViewPager; import android.text.Editable; import android.text.TextUtils; @@ -137,7 +138,7 @@ public class LibraryActivity private HorizontalScrollView mLimiterScroller; private ViewGroup mLimiterViews; - + private VanillaTabLayout mVanillaTabLayout; /** * The action to execute when a row is tapped. */ @@ -184,7 +185,6 @@ public class LibraryActivity mViewPager = pager; SharedPreferences settings = PlaybackService.getSettings(this); - pager.setOnPageChangeListener(pagerAdapter); View controls = getLayoutInflater().inflate(R.layout.actionbar_controls, null); mTitle = (TextView)controls.findViewById(R.id.title); @@ -201,6 +201,9 @@ public class LibraryActivity mPermissionRequest.setVisibility(View.VISIBLE); } + mVanillaTabLayout = (VanillaTabLayout)findViewById(R.id.sliding_tabs); + mVanillaTabLayout.inheritElevation(getActionBar()); + mVanillaTabLayout.setOnPageChangeListener(pagerAdapter); loadTabOrder(); int page = settings.getInt(PrefKeys.LIBRARY_PAGE, PrefDefaults.LIBRARY_PAGE); @@ -235,7 +238,8 @@ public class LibraryActivity private void loadTabOrder() { if (mPagerAdapter.loadTabOrder()) { - CompatHoneycomb.addActionBarTabs(this); + // Reinitializes all tabs + mVanillaTabLayout.setViewPager(mViewPager); } } @@ -1008,7 +1012,6 @@ public class LibraryActivity mCurrentAdapter = adapter; mLastActedId = LibraryAdapter.INVALID_ID; updateLimiterViews(); - CompatHoneycomb.selectTab(this, position); if (adapter != null && (adapter.getLimiter() == null || adapter.getMediaType() == MediaUtils.TYPE_FILE)) { // Save current page so it is opened on next startup. Don't save if // the page was expanded to, as the expanded page isn't the starting diff --git a/src/ch/blinkenlights/android/vanilla/LibraryPagerAdapter.java b/src/ch/blinkenlights/android/vanilla/LibraryPagerAdapter.java index 92ccbc75..f056a716 100644 --- a/src/ch/blinkenlights/android/vanilla/LibraryPagerAdapter.java +++ b/src/ch/blinkenlights/android/vanilla/LibraryPagerAdapter.java @@ -812,7 +812,7 @@ public class LibraryPagerAdapter // onPageSelected and setPrimaryItem are called in similar cases, and it // would be nice to use just one of them, but each has caveats: // - onPageSelected isn't called when the ViewPager is first - // initialized + // initialized if there is no scrolling to do // - setPrimaryItem isn't called until scrolling is complete, which // makes tab bar and limiter updates look bad // So we use both.