Merge "[RenderScript] Enable developers to only use support runtime and use black list for certain devices." into mnc-dev
diff --git a/build.gradle b/build.gradle
index c7479cb..0c9b958 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,10 +9,10 @@
}
}
-ext.supportVersion = '22.1.1'
+ext.supportVersion = '22.2.0'
ext.extraVersion = 14
ext.supportRepoOut = ''
-ext.buildToolsVersion = '21.0.0'
+ext.buildToolsVersion = '22.1.0'
ext.buildNumber = Integer.toString(ext.extraVersion)
/*
diff --git a/design/api/current.txt b/design/api/current.txt
index 3cfe257..6053b92 100644
--- a/design/api/current.txt
+++ b/design/api/current.txt
@@ -17,10 +17,18 @@
method public boolean onNestedFling(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, float, float, boolean);
method public void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int, int, int[]);
method public void onNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, int, int, int, int);
+ method public void onRestoreInstanceState(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.os.Parcelable);
+ method public android.os.Parcelable onSaveInstanceState(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout);
method public boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View, android.view.View, int);
method public void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, android.support.design.widget.AppBarLayout, android.view.View);
}
+ protected static class AppBarLayout.Behavior.SavedState extends android.view.View.BaseSavedState {
+ ctor public AppBarLayout.Behavior.SavedState(android.os.Parcel);
+ ctor public AppBarLayout.Behavior.SavedState(android.os.Parcelable);
+ field public static final android.os.Parcelable.Creator<android.support.design.widget.AppBarLayout.Behavior.SavedState> CREATOR;
+ }
+
public static class AppBarLayout.LayoutParams extends android.widget.LinearLayout.LayoutParams {
ctor public AppBarLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
ctor public AppBarLayout.LayoutParams(int, int);
@@ -127,6 +135,8 @@
method public void onNestedPreScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int[]);
method public void onNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, int, int, int, int);
method public void onNestedScrollAccepted(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int);
+ method public void onRestoreInstanceState(android.support.design.widget.CoordinatorLayout, V, android.os.Parcelable);
+ method public android.os.Parcelable onSaveInstanceState(android.support.design.widget.CoordinatorLayout, V);
method public boolean onStartNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View, android.view.View, int);
method public void onStopNestedScroll(android.support.design.widget.CoordinatorLayout, V, android.view.View);
method public boolean onTouchEvent(android.support.design.widget.CoordinatorLayout, V, android.view.MotionEvent);
@@ -150,6 +160,12 @@
field public int keyline;
}
+ protected static class CoordinatorLayout.SavedState extends android.view.View.BaseSavedState {
+ ctor public CoordinatorLayout.SavedState(android.os.Parcel);
+ ctor public CoordinatorLayout.SavedState(android.os.Parcelable);
+ field public static final android.os.Parcelable.Creator<android.support.design.widget.CoordinatorLayout.SavedState> CREATOR;
+ }
+
public class FloatingActionButton extends android.widget.ImageView {
ctor public FloatingActionButton(android.content.Context);
ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet);
@@ -239,8 +255,6 @@
method public void addTab(android.support.design.widget.TabLayout.Tab, int);
method public void addTab(android.support.design.widget.TabLayout.Tab, boolean);
method public void addTab(android.support.design.widget.TabLayout.Tab, int, boolean);
- method public void addTabsFromPagerAdapter(android.support.v4.view.PagerAdapter);
- method public android.support.v4.view.ViewPager.OnPageChangeListener createOnPageChangeListener();
method public android.support.design.widget.TabLayout.Tab getTabAt(int);
method public int getTabCount();
method public int getTabGravity();
@@ -256,6 +270,8 @@
method public void setTabMode(int);
method public void setTabTextColors(android.content.res.ColorStateList);
method public void setTabTextColors(int, int);
+ method public void setTabsFromPagerAdapter(android.support.v4.view.PagerAdapter);
+ method public void setupWithViewPager(android.support.v4.view.ViewPager);
field public static final int GRAVITY_CENTER = 1; // 0x1
field public static final int GRAVITY_FILL = 0; // 0x0
field public static final int MODE_FIXED = 1; // 0x1
@@ -287,10 +303,23 @@
field public static final int INVALID_POSITION = -1; // 0xffffffff
}
+ public static class TabLayout.TabLayoutOnPageChangeListener implements android.support.v4.view.ViewPager.OnPageChangeListener {
+ ctor public TabLayout.TabLayoutOnPageChangeListener(android.support.design.widget.TabLayout);
+ method public void onPageScrollStateChanged(int);
+ method public void onPageScrolled(int, float, int);
+ method public void onPageSelected(int);
+ }
+
+ public static class TabLayout.ViewPagerOnTabSelectedListener implements android.support.design.widget.TabLayout.OnTabSelectedListener {
+ ctor public TabLayout.ViewPagerOnTabSelectedListener(android.support.v4.view.ViewPager);
+ method public void onTabReselected(android.support.design.widget.TabLayout.Tab);
+ method public void onTabSelected(android.support.design.widget.TabLayout.Tab);
+ method public void onTabUnselected(android.support.design.widget.TabLayout.Tab);
+ }
+
public class TextInputLayout extends android.widget.LinearLayout {
ctor public TextInputLayout(android.content.Context);
ctor public TextInputLayout(android.content.Context, android.util.AttributeSet);
- ctor public TextInputLayout(android.content.Context, android.util.AttributeSet, int);
method public android.widget.EditText getEditText();
method public void setError(java.lang.CharSequence);
method public void setErrorEnabled(boolean);
diff --git a/design/res-public/values/public_attrs.xml b/design/res-public/values/public_attrs.xml
index b2ad8fd..51c7b4d 100644
--- a/design/res-public/values/public_attrs.xml
+++ b/design/res-public/values/public_attrs.xml
@@ -27,6 +27,7 @@
<public type="attr" name="expandedTitleMarginBottom"/>
<public type="attr" name="expandedTitleMarginEnd"/>
<public type="attr" name="expandedTitleMarginStart"/>
+ <public type="attr" name="expandedTitleMarginTop"/>
<public type="attr" name="expandedTitleTextAppearance"/>
<public type="attr" name="fabSize"/>
<public type="attr" name="headerLayout"/>
@@ -56,4 +57,5 @@
<public type="attr" name="tabSelectedTextColor"/>
<public type="attr" name="tabTextAppearance"/>
<public type="attr" name="tabTextColor"/>
+ <public type="attr" name="toolbarId"/>
</resources>
diff --git a/design/res/values/attrs.xml b/design/res/values/attrs.xml
index e2afe99..a89d330 100644
--- a/design/res/values/attrs.xml
+++ b/design/res/values/attrs.xml
@@ -205,11 +205,22 @@
</declare-styleable>
<declare-styleable name="CollapsingToolbarLayout">
+ <!-- Specifies extra space on the start, top, end and bottom
+ sides of the the expanded title text. Margin values should be positive. -->
<attr name="expandedTitleMargin" format="dimension"/>
+ <!-- Specifies extra space on the start side of the the expanded title text.
+ Margin values should be positive. -->
<attr name="expandedTitleMarginStart" format="dimension"/>
- <attr name="expandedTitleMarginBottom" format="dimension"/>
+ <!-- Specifies extra space on the top side of the the expanded title text.
+ Margin values should be positive. -->
+ <attr name="expandedTitleMarginTop" format="dimension"/>
+ <!-- Specifies extra space on the end side of the the expanded title text.
+ Margin values should be positive. -->
<attr name="expandedTitleMarginEnd" format="dimension"/>
- <!-- The text appearance of the CollapsingToolbarLayouts title when it is fully
+ <!-- Specifies extra space on the bottom side of the the expanded title text.
+ Margin values should be positive. -->
+ <attr name="expandedTitleMarginBottom" format="dimension"/>
+ <!-- The text appearance of the CollapsingToolbarLayout's title when it is fully
'expanded' -->
<attr name="expandedTitleTextAppearance" format="reference"/>
<!-- The text appearance of the CollapsingToolbarLayouts title when it is fully
@@ -222,6 +233,9 @@
CollapsingToolbarLayout has been scrolled sufficiently off screen. Only works on
Lollipop with the correct setup. -->
<attr name="statusBarScrim" format="color" />
+ <!-- The id of the primary Toolbar child that you wish to use for the purpose of collapsing.
+ If you do not set this then the first Toolbar child found will be used. -->
+ <attr name="toolbarId" format="reference"/>
</declare-styleable>
<declare-styleable name="CollapsingAppBarLayout_LayoutParams">
diff --git a/design/src/android/support/design/internal/NavigationMenuPresenter.java b/design/src/android/support/design/internal/NavigationMenuPresenter.java
index e784bd5..f84b567 100644
--- a/design/src/android/support/design/internal/NavigationMenuPresenter.java
+++ b/design/src/android/support/design/internal/NavigationMenuPresenter.java
@@ -282,7 +282,7 @@
NavigationMenuItemView itemView = (NavigationMenuItemView) convertView;
itemView.setIconTintList(mIconTintList);
itemView.setTextColor(mTextColor);
- itemView.setBackground(mItemBackground);
+ itemView.setBackgroundDrawable(mItemBackground);
itemView.initialize(item.getMenuItem(), 0);
break;
case VIEW_TYPE_SUBHEADER:
diff --git a/design/src/android/support/design/widget/AppBarLayout.java b/design/src/android/support/design/widget/AppBarLayout.java
index bb77821..b15c03a 100644
--- a/design/src/android/support/design/widget/AppBarLayout.java
+++ b/design/src/android/support/design/widget/AppBarLayout.java
@@ -18,6 +18,8 @@
import android.content.Context;
import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.support.annotation.IntDef;
import android.support.design.R;
import android.support.v4.view.ViewCompat;
@@ -579,7 +581,9 @@
* scroll handling with offsetting.
*/
public static class Behavior extends ViewOffsetBehavior<AppBarLayout> {
- private int mLogicalOffsetTop;
+ private static final int INVALID_POSITION = -1;
+
+ private int mOffsetDelta;
private boolean mSkipNestedPreScroll;
private Runnable mFlingRunnable;
@@ -587,6 +591,10 @@
private ValueAnimatorCompat mAnimator;
+ private int mOffsetToChildIndexOnLayout = INVALID_POSITION;
+ private boolean mOffsetToChildIndexOnLayoutIsMinHeight;
+ private float mOffsetToChildIndexOnLayoutPerc;
+
public Behavior() {}
public Behavior(Context context, AttributeSet attrs) {
@@ -682,7 +690,7 @@
}
}
- if (mLogicalOffsetTop != targetScroll) {
+ if (getTopBottomOffsetForScrollingSibling() != targetScroll) {
animateOffsetTo(coordinatorLayout, child, targetScroll);
return true;
}
@@ -722,7 +730,7 @@
}
mScroller.fling(
- 0, mLogicalOffsetTop, // curr
+ 0, getTopBottomOffsetForScrollingSibling(), // curr
0, Math.round(velocityY), // velocity.
0, 0, // x
minOffset, maxOffset); // y
@@ -758,12 +766,24 @@
}
@Override
- public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout child,
+ public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout appBarLayout,
int layoutDirection) {
- boolean handled = super.onLayoutChild(parent, child, layoutDirection);
+ boolean handled = super.onLayoutChild(parent, appBarLayout, layoutDirection);
+
+ if (mOffsetToChildIndexOnLayout >= 0) {
+ View child = appBarLayout.getChildAt(mOffsetToChildIndexOnLayout);
+ int offset = -child.getBottom();
+ if (mOffsetToChildIndexOnLayoutIsMinHeight) {
+ offset += ViewCompat.getMinimumHeight(child);
+ } else {
+ offset += Math.round(child.getHeight() * mOffsetToChildIndexOnLayoutPerc);
+ }
+ setTopAndBottomOffset(offset);
+ mOffsetToChildIndexOnLayout = INVALID_POSITION;
+ }
// Make sure we update the elevation
- dispatchOffsetUpdates(child);
+ dispatchOffsetUpdates(appBarLayout);
return handled;
}
@@ -771,7 +791,7 @@
private int scroll(CoordinatorLayout coordinatorLayout, AppBarLayout appBarLayout,
int dy, int minOffset, int maxOffset) {
return setAppBarTopBottomOffset(coordinatorLayout, appBarLayout,
- mLogicalOffsetTop - dy, minOffset, maxOffset);
+ getTopBottomOffsetForScrollingSibling() - dy, minOffset, maxOffset);
}
final int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout,
@@ -782,7 +802,7 @@
final int setAppBarTopBottomOffset(CoordinatorLayout coordinatorLayout,
AppBarLayout appBarLayout, int newOffset, int minOffset, int maxOffset) {
- final int curOffset = mLogicalOffsetTop;
+ final int curOffset = getTopBottomOffsetForScrollingSibling();
int consumed = 0;
if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) {
@@ -791,15 +811,16 @@
newOffset = MathUtils.constrain(newOffset, minOffset, maxOffset);
if (curOffset != newOffset) {
- boolean offsetChanged = setTopAndBottomOffset(
- appBarLayout.hasChildWithInterpolator()
- ? interpolateOffset(appBarLayout, newOffset)
- : newOffset);
+ final int interpolatedOffset = appBarLayout.hasChildWithInterpolator()
+ ? interpolateOffset(appBarLayout, newOffset)
+ : newOffset;
+
+ boolean offsetChanged = setTopAndBottomOffset(interpolatedOffset);
// Update how much dy we have consumed
consumed = curOffset - newOffset;
// Update the stored sibling offset
- mLogicalOffsetTop = newOffset;
+ mOffsetDelta = newOffset - interpolatedOffset;
if (!offsetChanged && appBarLayout.hasChildWithInterpolator()) {
// If the offset hasn't changed and we're using an interpolated scroll
@@ -873,7 +894,84 @@
}
final int getTopBottomOffsetForScrollingSibling() {
- return mLogicalOffsetTop;
+ return getTopAndBottomOffset() + mOffsetDelta;
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState(CoordinatorLayout parent, AppBarLayout appBarLayout) {
+ final Parcelable superState = super.onSaveInstanceState(parent, appBarLayout);
+ final int offset = getTopAndBottomOffset();
+
+ // Try and find the first visible child...
+ for (int i = 0, count = appBarLayout.getChildCount(); i < count; i++) {
+ View child = appBarLayout.getChildAt(i);
+ final int visBottom = child.getBottom() + offset;
+
+ if (child.getTop() + offset <= 0 && visBottom >= 0) {
+ final SavedState ss = new SavedState(superState);
+ ss.firstVisibleChildIndex = i;
+ ss.firstVisibileChildAtMinimumHeight =
+ visBottom == ViewCompat.getMinimumHeight(child);
+ ss.firstVisibileChildPercentageShown = visBottom / (float) child.getHeight();
+ return ss;
+ }
+ }
+
+ // Else we'll just return the super state
+ return superState;
+ }
+
+ @Override
+ public void onRestoreInstanceState(CoordinatorLayout parent, AppBarLayout appBarLayout,
+ Parcelable state) {
+ if (state instanceof SavedState) {
+ final SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(parent, appBarLayout, ss.getSuperState());
+ mOffsetToChildIndexOnLayout = ss.firstVisibleChildIndex;
+ mOffsetToChildIndexOnLayoutPerc = ss.firstVisibileChildPercentageShown;
+ mOffsetToChildIndexOnLayoutIsMinHeight = ss.firstVisibileChildAtMinimumHeight;
+ } else {
+ super.onRestoreInstanceState(parent, appBarLayout, state);
+ mOffsetToChildIndexOnLayout = INVALID_POSITION;
+ }
+ }
+
+ protected static class SavedState extends View.BaseSavedState {
+ int firstVisibleChildIndex;
+ float firstVisibileChildPercentageShown;
+ boolean firstVisibileChildAtMinimumHeight;
+
+ public SavedState(Parcel source) {
+ super(source);
+ firstVisibleChildIndex = source.readInt();
+ firstVisibileChildPercentageShown = source.readFloat();
+ firstVisibileChildAtMinimumHeight = source.readByte() != 0;
+ }
+
+ public SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(firstVisibleChildIndex);
+ dest.writeFloat(firstVisibileChildPercentageShown);
+ dest.writeByte((byte) (firstVisibileChildAtMinimumHeight ? 1 : 0));
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR =
+ new Parcelable.Creator<SavedState>() {
+ @Override
+ public SavedState createFromParcel(Parcel source) {
+ return new SavedState(source);
+ }
+
+ @Override
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
}
}
diff --git a/design/src/android/support/design/widget/CollapsingToolbarLayout.java b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
index e634322..474a0a8 100644
--- a/design/src/android/support/design/widget/CollapsingToolbarLayout.java
+++ b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
@@ -22,6 +22,7 @@
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.support.annotation.DrawableRes;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
@@ -51,10 +52,15 @@
* {@link #setTitle(CharSequence)}. The title appearance can be tweaked via the
* {@code collapsedTextAppearance} and {@code expandedTextAppearance} attributes.
*
- * <h3>Foreground scrim</h3>
+ * <h3>Content scrim</h3>
* A full-bleed scrim which is show or hidden when the scroll position has hit a certain threshold.
* You can change this via {@link #setContentScrim(Drawable)}.
*
+ * <h3>Status bar scrim</h3>
+ * A scrim which is show or hidden behind the status bar when the scroll position has hit a certain
+ * threshold. You can change this via {@link #setStatusBarScrim(Drawable)}. This only works
+ * on {@link Build.VERSION_CODES#LOLLIPOP LOLLIPOP} devices when we set to fit system windows.
+ *
* <h3>Parallax scrolling children</h3>
* Child views can opt to be scrolled within this layout in a parallax fashion.
* See {@link LayoutParams#COLLAPSE_MODE_PARALLAX} and
@@ -73,19 +79,23 @@
* @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginEnd
* @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_expandedTitleMarginBottom
* @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_statusBarScrim
+ * @attr ref android.support.design.R.styleable#CollapsingToolbarLayout_toolbarId
*/
public class CollapsingToolbarLayout extends FrameLayout {
private static final int SCRIM_ANIMATION_DURATION = 600;
+ private boolean mRefreshToolbar = true;
+ private int mToolbarId;
private Toolbar mToolbar;
private View mDummyView;
private int mExpandedMarginLeft;
+ private int mExpandedMarginTop;
private int mExpandedMarginRight;
private int mExpandedMarginBottom;
- private final Rect mRect = new Rect();
+ private final Rect mTmpRect = new Rect();
private final CollapsingTextHelper mCollapsingTextHelper;
private Drawable mContentScrim;
@@ -119,7 +129,7 @@
R.styleable.CollapsingToolbarLayout, defStyleAttr,
R.style.Widget_Design_CollapsingToolbar);
- mExpandedMarginLeft = mExpandedMarginRight = mExpandedMarginBottom =
+ mExpandedMarginLeft = mExpandedMarginTop = mExpandedMarginRight = mExpandedMarginBottom =
a.getDimensionPixelSize(R.styleable.CollapsingToolbarLayout_expandedTitleMargin, 0);
final boolean isRtl = ViewCompat.getLayoutDirection(this)
@@ -142,6 +152,10 @@
mExpandedMarginRight = marginEnd;
}
}
+ if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleMarginTop)) {
+ mExpandedMarginTop = a.getDimensionPixelSize(
+ R.styleable.CollapsingToolbarLayout_expandedTitleMarginTop, 0);
+ }
if (a.hasValue(R.styleable.CollapsingToolbarLayout_expandedTitleMarginBottom)) {
mExpandedMarginBottom = a.getDimensionPixelSize(
R.styleable.CollapsingToolbarLayout_expandedTitleMarginBottom, 0);
@@ -160,6 +174,8 @@
setContentScrim(a.getDrawable(R.styleable.CollapsingToolbarLayout_contentScrim));
setStatusBarScrim(a.getDrawable(R.styleable.CollapsingToolbarLayout_statusBarScrim));
+ mToolbarId = a.getResourceId(R.styleable.CollapsingToolbarLayout_toolbarId, -1);
+
a.recycle();
setWillNotDraw(false);
@@ -170,15 +186,7 @@
public WindowInsetsCompat onApplyWindowInsets(View v,
WindowInsetsCompat insets) {
mLastInsets = insets;
-
- for (int i = 0, z = getChildCount(); i < z; i++) {
- View child = getChildAt(i);
- LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
- if (lp.height != LayoutParams.MATCH_PARENT) {
- lp.topMargin = insets.getSystemWindowInsetTop();
- }
- }
+ requestLayout();
return insets.consumeSystemWindowInsets();
}
});
@@ -210,22 +218,12 @@
}
@Override
- public void addView(View child, int index, ViewGroup.LayoutParams params) {
- super.addView(child, index, params);
-
- if (child instanceof Toolbar) {
- mToolbar = (Toolbar) child;
- mDummyView = new View(getContext());
- mToolbar.addView(mDummyView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
- }
- }
-
- @Override
public void draw(Canvas canvas) {
super.draw(canvas);
// If we don't have a toolbar, the scrim will be not be drawn in drawChild() below.
// Instead, we draw it here, before our collapsing text.
+ ensureToolbar();
if (mToolbar == null && mContentScrim != null && mScrimAlpha > 0) {
mContentScrim.mutate().setAlpha(mScrimAlpha);
mContentScrim.draw(canvas);
@@ -251,6 +249,7 @@
// This is a little weird. Our scrim needs to be behind the Toolbar (if it is present),
// but in front of any other children which are behind it. To do this we intercept the
// drawChild() call, and draw our scrim first when drawing the toolbar
+ ensureToolbar();
if (child == mToolbar && mContentScrim != null && mScrimAlpha > 0) {
mContentScrim.mutate().setAlpha(mScrimAlpha);
mContentScrim.draw(canvas);
@@ -268,28 +267,91 @@
}
}
+ private void ensureToolbar() {
+ if (!mRefreshToolbar) {
+ return;
+ }
+
+ Toolbar fallback = null, selected = null;
+
+ for (int i = 0, count = getChildCount(); i < count; i++) {
+ final View child = getChildAt(i);
+ if (child instanceof Toolbar) {
+ if (mToolbarId != -1) {
+ // There's a toolbar id set so try and find it...
+ if (mToolbarId == child.getId()) {
+ // We found the primary Toolbar, use it
+ selected = (Toolbar) child;
+ break;
+ }
+ if (fallback == null) {
+ // We'll record the first Toolbar as our fallback
+ fallback = (Toolbar) child;
+ }
+ } else {
+ // We don't have a id to check for so just use the first we come across
+ selected = (Toolbar) child;
+ break;
+ }
+ }
+ }
+
+ if (selected == null) {
+ // If we didn't find a primary Toolbar, use the fallback
+ selected = fallback;
+ }
+
+ if (selected != null) {
+ mToolbar = selected;
+ mDummyView = new View(getContext());
+ mToolbar.addView(mDummyView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+ } else {
+ mToolbar = null;
+ mDummyView = null;
+ }
+ mRefreshToolbar = false;
+ }
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
// Update our child view offset helpers
for (int i = 0, z = getChildCount(); i < z; i++) {
- getViewOffsetHelper(getChildAt(i)).onViewLayout();
+ final View child = getChildAt(i);
+
+ if (mLastInsets != null && !ViewCompat.getFitsSystemWindows(child)) {
+ final int insetTop = mLastInsets.getSystemWindowInsetTop();
+ if (child.getTop() < insetTop) {
+ // If the child isn't set to fit system windows but is drawing within the inset
+ // offset it down
+ child.offsetTopAndBottom(insetTop);
+ }
+ }
+
+ getViewOffsetHelper(child).onViewLayout();
}
// Now let the collapsing text helper update itself
mCollapsingTextHelper.onLayout(changed, left, top, right, bottom);
+ ensureToolbar();
+
// Update the collapsed bounds by getting it's transformed bounds
- ViewGroupUtils.getDescendantRect(this, mDummyView, mRect);
- mCollapsingTextHelper.setCollapsedBounds(mRect.left, bottom - mRect.height(),
- mRect.right, bottom);
- // Update the expanded bounds
- mCollapsingTextHelper.setExpandedBounds(left + mExpandedMarginLeft, mDummyView.getBottom(),
- right - mExpandedMarginRight, bottom - mExpandedMarginBottom);
+ if (mDummyView != null) {
+ ViewGroupUtils.getDescendantRect(this, mDummyView, mTmpRect);
+ mCollapsingTextHelper.setCollapsedBounds(mTmpRect.left, bottom - mTmpRect.height(),
+ mTmpRect.right, bottom);
+ // Update the expanded bounds
+ mCollapsingTextHelper.setExpandedBounds(left + mExpandedMarginLeft,
+ mTmpRect.bottom + mExpandedMarginTop, right - mExpandedMarginRight,
+ bottom - mExpandedMarginBottom);
+ }
// Finally, set our minimum height to enable proper AppBarLayout collapsing
- setMinimumHeight(mToolbar.getHeight());
+ if (mToolbar != null) {
+ setMinimumHeight(mToolbar.getHeight());
+ }
}
private static ViewOffsetHelper getViewOffsetHelper(View view) {
@@ -325,6 +387,7 @@
}
private void animateScrim(int targetAlpha) {
+ ensureToolbar();
if (mScrimAnimator == null) {
mScrimAnimator = ViewUtils.createAnimator();
mScrimAnimator.setDuration(SCRIM_ANIMATION_DURATION);
diff --git a/design/src/android/support/design/widget/CoordinatorLayout.java b/design/src/android/support/design/widget/CoordinatorLayout.java
index 0003daa..aa1bab8 100644
--- a/design/src/android/support/design/widget/CoordinatorLayout.java
+++ b/design/src/android/support/design/widget/CoordinatorLayout.java
@@ -26,6 +26,8 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.SystemClock;
import android.support.design.R;
import android.support.v4.content.ContextCompat;
@@ -37,6 +39,7 @@
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.SparseArray;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
@@ -2043,6 +2046,44 @@
V child, WindowInsetsCompat insets) {
return insets;
}
+
+ /**
+ * Hook allowing a behavior to re-apply a representation of its internal state that had
+ * previously been generated by {@link #onSaveInstanceState}. This function will never
+ * be called with a null state.
+ *
+ * @param parent the parent CoordinatorLayout
+ * @param child child view to restore from
+ * @param state The frozen state that had previously been returned by
+ * {@link #onSaveInstanceState}.
+ *
+ * @see #onSaveInstanceState()
+ */
+ public void onRestoreInstanceState(CoordinatorLayout parent, V child, Parcelable state) {
+ // no-op
+ }
+
+ /**
+ * Hook allowing a behavior to generate a representation of its internal state
+ * that can later be used to create a new instance with that same state.
+ * This state should only contain information that is not persistent or can
+ * not be reconstructed later.
+ *
+ * <p>Behavior state is only saved when both the parent {@link CoordinatorLayout} and
+ * a view using this behavior have valid IDs set.</p>
+ *
+ * @param parent the parent CoordinatorLayout
+ * @param child child view to restore from
+ *
+ * @return Returns a Parcelable object containing the behavior's current dynamic
+ * state.
+ *
+ * @see #onRestoreInstanceState(android.os.Parcelable)
+ * @see View#onSaveInstanceState()
+ */
+ public Parcelable onSaveInstanceState(CoordinatorLayout parent, V child) {
+ return BaseSavedState.EMPTY_STATE;
+ }
}
/**
@@ -2413,4 +2454,106 @@
return insets.consumeSystemWindowInsets();
}
}
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ final SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+
+ final SparseArray<Parcelable> behaviorStates = ss.behaviorStates;
+
+ for (int i = 0, count = getChildCount(); i < count; i++) {
+ final View child = getChildAt(i);
+ final int childId = child.getId();
+ final LayoutParams lp = getResolvedLayoutParams(child);
+ final Behavior b = lp.getBehavior();
+
+ if (childId != NO_ID && b != null) {
+ Parcelable savedState = behaviorStates.get(childId);
+ if (savedState != null) {
+ b.onRestoreInstanceState(this, child, savedState);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ final SavedState ss = new SavedState(super.onSaveInstanceState());
+
+ final SparseArray<Parcelable> behaviorStates = new SparseArray<>();
+ for (int i = 0, count = getChildCount(); i < count; i++) {
+ final View child = getChildAt(i);
+ final int childId = child.getId();
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ final Behavior b = lp.getBehavior();
+
+ if (childId != NO_ID && b != null) {
+ // If the child has an ID and a Behavior, let it save some state...
+ Parcelable state = b.onSaveInstanceState(this, child);
+ if (state != null) {
+ behaviorStates.append(childId, state);
+ }
+ }
+ }
+ ss.behaviorStates = behaviorStates;
+ return ss;
+ }
+
+ protected static class SavedState extends BaseSavedState {
+ SparseArray<Parcelable> behaviorStates;
+
+ public SavedState(Parcel source) {
+ super(source);
+
+ final int size = source.readInt();
+
+ final int[] ids = new int[size];
+ source.readIntArray(ids);
+
+ final Parcelable[] states = source.readParcelableArray(
+ CoordinatorLayout.class.getClassLoader());
+
+ behaviorStates = new SparseArray<>(size);
+ for (int i = 0; i < size; i++) {
+ behaviorStates.append(ids[i], states[i]);
+ }
+ }
+
+ public SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+
+ final int size = behaviorStates != null ? behaviorStates.size() : 0;
+ dest.writeInt(size);
+
+ final int[] ids = new int[size];
+ final Parcelable[] states = new Parcelable[size];
+
+ for (int i = 0; i < size; i++) {
+ ids[i] = behaviorStates.keyAt(i);
+ states[i] = behaviorStates.valueAt(i);
+ }
+ dest.writeIntArray(ids);
+ dest.writeParcelableArray(states, flags);
+
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR =
+ new Parcelable.Creator<SavedState>() {
+ @Override
+ public SavedState createFromParcel(Parcel source) {
+ return new SavedState(source);
+ }
+
+ @Override
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
}
diff --git a/design/src/android/support/design/widget/TabLayout.java b/design/src/android/support/design/widget/TabLayout.java
index d743340..3e5cd97 100755
--- a/design/src/android/support/design/widget/TabLayout.java
+++ b/design/src/android/support/design/widget/TabLayout.java
@@ -52,6 +52,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
@@ -71,14 +72,14 @@
* notified when any tab's selection state has been changed.
* <p>
* If you're using a {@link android.support.v4.view.ViewPager} together
- * with this layout, you can use {@link #addTabsFromPagerAdapter(PagerAdapter)} which will populate
- * the tabs using the {@link PagerAdapter}'s page titles. You should also use a {@link
- * ViewPager.OnPageChangeListener} to forward the scroll and selection changes to this layout.
- * You can use the one returned {@link #createOnPageChangeListener()} for easy implementation:
+ * with this layout, you can use {@link #setTabsFromPagerAdapter(PagerAdapter)} which will populate
+ * the tabs using the given {@link PagerAdapter}'s page titles. You should also use a
+ * {@link TabLayoutOnPageChangeListener} to forward the scroll and selection changes to this
+ * layout like so:
* <pre>
* ViewPager viewPager = ...;
* TabLayout tabLayout = ...;
- * viewPager.setOnPageChangeListener(tabLayout.createOnPageChangeListener());
+ * viewPager.addOnPageChangeListener(new TabLayoutOnPageChangeListener(tabLayout));
* </pre>
*
* @see <a href="http://www.google.com/design/spec/components/tabs.html">Tabs</a>
@@ -271,7 +272,7 @@
/**
* Set the scroll position of the tabs. This is useful for when the tabs are being displayed as
- * part of a scrolling container such as {@link ViewPager}.
+ * part of a scrolling container such as {@link android.support.v4.view.ViewPager}.
* <p>
* Calling this method does not update the selected tab, it is only used for drawing purposes.
*
@@ -298,51 +299,6 @@
}
/**
- * Add new {@link Tab}s populated from a {@link PagerAdapter}. Each tab will have it's text set
- * to the value returned from {@link PagerAdapter#getPageTitle(int)}.
- *
- * @param adapter the adapter to populate from
- */
- public void addTabsFromPagerAdapter(PagerAdapter adapter) {
- for (int i = 0, count = adapter.getCount(); i < count; i++) {
- addTab(newTab().setText(adapter.getPageTitle(i)));
- }
- }
-
- /**
- * Create a {@link ViewPager.OnPageChangeListener} which implements the
- * necessary calls back to this layout so that the tabs position is kept in sync.
- * <p>
- * If you need to have a custom {@link ViewPager.OnPageChangeListener} for your own
- * purposes, you can still use the instance returned from this method, but making sure to call
- * through to all of the methods.
- */
- public ViewPager.OnPageChangeListener createOnPageChangeListener() {
- return new ViewPager.OnPageChangeListener() {
- private int mScrollState;
-
- @Override
- public void onPageScrollStateChanged(int state) {
- mScrollState = state;
- }
-
- @Override
- public void onPageScrolled(int position, float positionOffset,
- int positionOffsetPixels) {
- // Update the scroll position, only update the text selection if we're being
- // dragged
- setScrollPosition(position, positionOffset,
- mScrollState == ViewPager.SCROLL_STATE_DRAGGING);
- }
-
- @Override
- public void onPageSelected(int position) {
- getTabAt(position).select();
- }
- };
- }
-
- /**
* Add a tab to this layout. The tab will be added at the end of the list.
* If this is the first tab to be added it will become the selected tab.
*
@@ -401,7 +357,8 @@
}
/**
- * Set the {@link android.support.design.widget.TabLayout.OnTabSelectedListener} that will handle switching to and from tabs.
+ * Set the {@link android.support.design.widget.TabLayout.OnTabSelectedListener} that will
+ * handle switching to and from tabs.
*
* @param onTabSelectedListener Listener to handle tab selection events
*/
@@ -497,7 +454,7 @@
* <li>{@link #MODE_SCROLLABLE}: Scrollable tabs display a subset of tabs at any given moment,
* and can contain longer tab labels and a larger number of tabs. They are best used for
* browsing contexts in touch interfaces when users don’t need to directly compare the tab
- * labels. This mode is commonly used with a {@link ViewPager}.</li>
+ * labels. This mode is commonly used with a {@link android.support.v4.view.ViewPager}.</li>
* </ul>
*
* @param mode one of {@link #MODE_FIXED} or {@link #MODE_SCROLLABLE}.
@@ -565,6 +522,55 @@
setTabTextColors(createColorStateList(normalColor, selectedColor));
}
+ /**
+ * The one-stop shop for setting up this {@link TabLayout} with a {@link ViewPager}.
+ *
+ * <p>This method will:
+ * <ul>
+ * <li>Add a {@link ViewPager.OnPageChangeListener} that will forward events to
+ * this TabLayout.</li>
+ * <li>Populate the TabLayout's tabs from the ViewPager's {@link PagerAdapter}.</li>
+ * <li>Set our {@link TabLayout.OnTabSelectedListener} which will forward
+ * selected events to the ViewPager</li>
+ * </ul>
+ * </p>
+ *
+ * @see #setTabsFromPagerAdapter(PagerAdapter)
+ * @see TabLayoutOnPageChangeListener
+ * @see ViewPagerOnTabSelectedListener
+ */
+ public void setupWithViewPager(ViewPager viewPager) {
+ final PagerAdapter adapter = viewPager.getAdapter();
+ if (adapter == null) {
+ throw new IllegalArgumentException("ViewPager does not have a PagerAdapter set");
+ }
+
+ // First we'll add Tabs, using the adapter's page titles
+ setTabsFromPagerAdapter(adapter);
+
+ // Now we'll add our page change listener to the ViewPager
+ viewPager.addOnPageChangeListener(new TabLayoutOnPageChangeListener(this));
+
+ // Now we'll add a tab selected listener to set ViewPager's current item
+ setOnTabSelectedListener(new ViewPagerOnTabSelectedListener(viewPager));
+ }
+
+ /**
+ * Populate our tab content from the given {@link PagerAdapter}.
+ * <p>
+ * Any existing tabs will be removed first. Each tab will have it's text set to the value
+ * returned from {@link PagerAdapter#getPageTitle(int)}
+ * </p>
+ *
+ * @param adapter the adapter to populate from
+ */
+ public void setTabsFromPagerAdapter(PagerAdapter adapter) {
+ removeAllTabs();
+ for (int i = 0, count = adapter.getCount(); i < count; i++) {
+ addTab(newTab().setText(adapter.getPageTitle(i)));
+ }
+ }
+
private void updateAllTabs() {
for (int i = 0, z = mTabStrip.getChildCount(); i < z; i++) {
updateTab(i);
@@ -1421,4 +1427,75 @@
}
}
+ /**
+ * A {@link ViewPager.OnPageChangeListener} class which contains the
+ * necessary calls back to the provided {@link TabLayout} so that the tab position is
+ * kept in sync.
+ *
+ * <p>This class stores the provided TabLayout weakly, meaning that you can use
+ * {@link ViewPager#addOnPageChangeListener(ViewPager.OnPageChangeListener)
+ * addOnPageChangeListener(OnPageChangeListener)} without removing the listener and
+ * not cause a leak.
+ */
+ public static class TabLayoutOnPageChangeListener implements ViewPager.OnPageChangeListener {
+ private final WeakReference<TabLayout> mTabLayoutRef;
+ private int mScrollState;
+
+ public TabLayoutOnPageChangeListener(TabLayout tabLayout) {
+ mTabLayoutRef = new WeakReference<>(tabLayout);
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ mScrollState = state;
+ }
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset,
+ int positionOffsetPixels) {
+ final TabLayout tabLayout = mTabLayoutRef.get();
+ if (tabLayout != null) {
+ // Update the scroll position, only update the text selection if we're being
+ // dragged
+ tabLayout.setScrollPosition(position, positionOffset,
+ mScrollState == ViewPager.SCROLL_STATE_DRAGGING);
+ }
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ final TabLayout tabLayout = mTabLayoutRef.get();
+ if (tabLayout != null) {
+ tabLayout.getTabAt(position).select();
+ }
+ }
+ }
+
+ /**
+ * A {@link TabLayout.OnTabSelectedListener} class which contains the necessary calls back
+ * to the provided {@link ViewPager} so that the tab position is kept in sync.
+ */
+ public static class ViewPagerOnTabSelectedListener implements TabLayout.OnTabSelectedListener {
+ private final ViewPager mViewPager;
+
+ public ViewPagerOnTabSelectedListener(ViewPager viewPager) {
+ mViewPager = viewPager;
+ }
+
+ @Override
+ public void onTabSelected(TabLayout.Tab tab) {
+ mViewPager.setCurrentItem(tab.getPosition());
+ }
+
+ @Override
+ public void onTabUnselected(TabLayout.Tab tab) {
+ // No-op
+ }
+
+ @Override
+ public void onTabReselected(TabLayout.Tab tab) {
+ // No-op
+ }
+ }
+
}
diff --git a/design/src/android/support/design/widget/TextInputLayout.java b/design/src/android/support/design/widget/TextInputLayout.java
index 364607b..83ed468 100644
--- a/design/src/android/support/design/widget/TextInputLayout.java
+++ b/design/src/android/support/design/widget/TextInputLayout.java
@@ -39,8 +39,6 @@
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AccelerateInterpolator;
-import android.view.animation.Animation;
-import android.view.animation.Transformation;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -73,11 +71,7 @@
}
public TextInputLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TextInputLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
+ super(context, attrs);
setOrientation(VERTICAL);
setWillNotDraw(false);
@@ -100,7 +94,7 @@
mCollapsingTextHelper.setCollapsedTextVerticalGravity(Gravity.TOP);
final TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.TextInputLayout, defStyleAttr, R.style.Widget_Design_TextInputLayout);
+ R.styleable.TextInputLayout, 0, R.style.Widget_Design_TextInputLayout);
mHint = a.getText(R.styleable.TextInputLayout_android_hint);
final int hintAppearance = a.getResourceId(
diff --git a/design/src/android/support/design/widget/ViewOffsetBehavior.java b/design/src/android/support/design/widget/ViewOffsetBehavior.java
index a89db5c..3ffc744 100644
--- a/design/src/android/support/design/widget/ViewOffsetBehavior.java
+++ b/design/src/android/support/design/widget/ViewOffsetBehavior.java
@@ -17,7 +17,6 @@
package android.support.design.widget;
import android.content.Context;
-import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.View;
@@ -28,6 +27,9 @@
private ViewOffsetHelper mViewOffsetHelper;
+ private int mTempTopBottomOffset = 0;
+ private int mTempLeftRightOffset = 0;
+
public ViewOffsetBehavior() {}
public ViewOffsetBehavior(Context context, AttributeSet attrs) {
@@ -44,12 +46,23 @@
}
mViewOffsetHelper.onViewLayout();
+ if (mTempTopBottomOffset != 0) {
+ mViewOffsetHelper.setTopAndBottomOffset(mTempTopBottomOffset);
+ mTempTopBottomOffset = 0;
+ }
+ if (mTempLeftRightOffset != 0) {
+ mViewOffsetHelper.setLeftAndRightOffset(mTempLeftRightOffset);
+ mTempLeftRightOffset = 0;
+ }
+
return true;
}
public boolean setTopAndBottomOffset(int offset) {
if (mViewOffsetHelper != null) {
return mViewOffsetHelper.setTopAndBottomOffset(offset);
+ } else {
+ mTempTopBottomOffset = offset;
}
return false;
}
@@ -57,6 +70,8 @@
public boolean setLeftAndRightOffset(int offset) {
if (mViewOffsetHelper != null) {
return mViewOffsetHelper.setLeftAndRightOffset(offset);
+ } else {
+ mTempLeftRightOffset = offset;
}
return false;
}
diff --git a/percent/Android.mk b/percent/Android.mk
new file mode 100644
index 0000000..5d4cf86
--- /dev/null
+++ b/percent/Android.mk
@@ -0,0 +1,48 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+# Build the resources using the current SDK version.
+# We do this here because the final static library must be compiled with an older
+# SDK version than the resources. The resources library and the R class that it
+# contains will not be linked into the final static library.
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-percent-res
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, dummy)
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_JAR_EXCLUDE_FILES := none
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Here is the final static library that apps can link against.
+# The R class is automatically excluded from the generated library.
+# Applications that use this library must specify LOCAL_RESOURCE_DIR
+# in their makefiles to include the resources in their package.
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-support-percent
+LOCAL_SDK_VERSION := 7
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_LIBRARIES := android-support-percent-res \
+ android-support-v4
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# API Check
+# ---------------------------------------------
+support_module := $(LOCAL_MODULE)
+support_module_api_dir := $(LOCAL_PATH)/api
+support_module_src_files := $(LOCAL_SRC_FILES)
+support_module_java_libraries := $(LOCAL_JAVA_LIBRARIES)
+support_module_java_packages := android.support.percent
+include $(SUPPORT_API_CHECK)
diff --git a/percent/AndroidManifest.xml b/percent/AndroidManifest.xml
new file mode 100644
index 0000000..12a6568
--- /dev/null
+++ b/percent/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.support.percent">
+ <uses-sdk android:minSdkVersion="7"/>
+ <application />
+</manifest>
diff --git a/percent/api/current.txt b/percent/api/current.txt
new file mode 100644
index 0000000..34d0302
--- /dev/null
+++ b/percent/api/current.txt
@@ -0,0 +1,64 @@
+package android.support.percent {
+
+ public class PercentFrameLayout extends android.widget.FrameLayout {
+ ctor public PercentFrameLayout(android.content.Context);
+ ctor public PercentFrameLayout(android.content.Context, android.util.AttributeSet);
+ ctor public PercentFrameLayout(android.content.Context, android.util.AttributeSet, int);
+ }
+
+ public static class PercentFrameLayout.LayoutParams extends android.widget.FrameLayout.LayoutParams implements android.support.percent.PercentLayoutHelper.PercentLayoutParams {
+ ctor public PercentFrameLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+ ctor public PercentFrameLayout.LayoutParams(int, int);
+ ctor public PercentFrameLayout.LayoutParams(int, int, int);
+ ctor public PercentFrameLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+ ctor public PercentFrameLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+ ctor public PercentFrameLayout.LayoutParams(android.widget.FrameLayout.LayoutParams);
+ ctor public PercentFrameLayout.LayoutParams(android.support.percent.PercentFrameLayout.LayoutParams);
+ method public android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+ }
+
+ public class PercentLayoutHelper {
+ ctor public PercentLayoutHelper(android.view.ViewGroup);
+ method public void adjustChildren(int, int);
+ method public static void fetchWidthAndHeight(android.view.ViewGroup.LayoutParams, android.content.res.TypedArray, int, int);
+ method public static android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo(android.content.Context, android.util.AttributeSet);
+ method public boolean handleMeasuredStateTooSmall();
+ method public void restoreOriginalParams();
+ }
+
+ public static class PercentLayoutHelper.PercentLayoutInfo {
+ ctor public PercentLayoutHelper.PercentLayoutInfo();
+ method public void fillLayoutParams(android.view.ViewGroup.LayoutParams, int, int);
+ method public void fillMarginLayoutParams(android.view.ViewGroup.MarginLayoutParams, int, int);
+ method public void restoreLayoutParams(android.view.ViewGroup.LayoutParams);
+ method public void restoreMarginLayoutParams(android.view.ViewGroup.MarginLayoutParams);
+ field public float bottomMarginPercent;
+ field public float endMarginPercent;
+ field public float heightPercent;
+ field public float leftMarginPercent;
+ field public float rightMarginPercent;
+ field public float startMarginPercent;
+ field public float topMarginPercent;
+ field public float widthPercent;
+ }
+
+ public static abstract interface PercentLayoutHelper.PercentLayoutParams {
+ method public abstract android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+ }
+
+ public class PercentRelativeLayout extends android.widget.RelativeLayout {
+ ctor public PercentRelativeLayout(android.content.Context);
+ ctor public PercentRelativeLayout(android.content.Context, android.util.AttributeSet);
+ ctor public PercentRelativeLayout(android.content.Context, android.util.AttributeSet, int);
+ }
+
+ public static class PercentRelativeLayout.LayoutParams extends android.widget.RelativeLayout.LayoutParams implements android.support.percent.PercentLayoutHelper.PercentLayoutParams {
+ ctor public PercentRelativeLayout.LayoutParams(android.content.Context, android.util.AttributeSet);
+ ctor public PercentRelativeLayout.LayoutParams(int, int);
+ ctor public PercentRelativeLayout.LayoutParams(android.view.ViewGroup.LayoutParams);
+ ctor public PercentRelativeLayout.LayoutParams(android.view.ViewGroup.MarginLayoutParams);
+ method public android.support.percent.PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo();
+ }
+
+}
+
diff --git a/percent/api/removed.txt b/percent/api/removed.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/percent/api/removed.txt
diff --git a/percent/build.gradle b/percent/build.gradle
new file mode 100644
index 0000000..28907b8
--- /dev/null
+++ b/percent/build.gradle
@@ -0,0 +1,30 @@
+apply plugin: 'android-library'
+
+archivesBaseName = 'percent'
+
+dependencies {
+ compile project(':support-v4')
+}
+
+android {
+ compileSdkVersion 'current'
+
+ sourceSets {
+ main.manifest.srcFile 'AndroidManifest.xml'
+ main.java.srcDirs = ['src']
+ main.res.srcDir 'res'
+ main.assets.srcDir 'assets'
+ main.resources.srcDir 'src'
+
+ // this moves src/instrumentTest to tests so all folders follow:
+ // tests/java, tests/res, tests/assets, ...
+ // This is a *reset* so it replaces the default paths
+ androidTest.setRoot('tests')
+ androidTest.java.srcDir 'tests/src'
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_7
+ targetCompatibility JavaVersion.VERSION_1_7
+ }
+}
diff --git a/percent/res/values/attrs.xml b/percent/res/values/attrs.xml
new file mode 100644
index 0000000..31154cc
--- /dev/null
+++ b/percent/res/values/attrs.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ 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.
+-->
+<resources>
+ <declare-styleable name="PercentLayout_Layout">
+ <attr name="layout_widthPercent" format="fraction"/>
+
+ <attr name="layout_heightPercent" format="fraction"/>
+
+ <attr name="layout_marginPercent" format="fraction"/>
+ <attr name="layout_marginLeftPercent" format="fraction"/>
+ <attr name="layout_marginTopPercent" format="fraction"/>
+ <attr name="layout_marginRightPercent" format="fraction"/>
+ <attr name="layout_marginBottomPercent" format="fraction"/>
+ <attr name="layout_marginStartPercent" format="fraction"/>
+ <attr name="layout_marginEndPercent" format="fraction"/>
+ </declare-styleable>
+</resources>
+
diff --git a/percent/src/.readme b/percent/src/.readme
new file mode 100644
index 0000000..4bcebad
--- /dev/null
+++ b/percent/src/.readme
@@ -0,0 +1,2 @@
+This hidden file is there to ensure there is an src folder.
+Once we support binary library this will go away.
\ No newline at end of file
diff --git a/percent/src/android/support/percent/PercentFrameLayout.java b/percent/src/android/support/percent/PercentFrameLayout.java
new file mode 100644
index 0000000..994be4c
--- /dev/null
+++ b/percent/src/android/support/percent/PercentFrameLayout.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * 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.percent;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+/**
+ * Subclass of {@link android.widget.FrameLayout} that supports percentage based dimensions and
+ * margins.
+ *
+ * You can specify dimension or a margin of child by using attributes with "Percent" suffix. Follow
+ * this example:
+ *
+ * <pre class="prettyprint">
+ * <android.support.percent.PercentFrameLayout
+ * xmlns:android="http://schemas.android.com/apk/res/android"
+ * xmlns:app="http://schemas.android.com/apk/res-auto"
+ * android:layout_width="match_parent"
+ * android:layout_height="match_parent"/>
+ * <ImageView
+ * app:layout_widthPercent="50%"
+ * app:layout_heightPercent="50%"
+ * app:layout_marginTopPercent="25%"
+ * app:layout_marginLeftPercent="25%"/>
+ * </android.support.percent.PercentFrameLayout/>
+ * </pre>
+ *
+ * The attributes that you can use are:
+ * <ul>
+ * <li>{@code layout_widthPercent}
+ * <li>{@code layout_heightPercent}
+ * <li>{@code layout_marginPercent}
+ * <li>{@code layout_marginLeftPercent}
+ * <li>{@code layout_marginTopPercent}
+ * <li>{@code layout_marginRightPercent}
+ * <li>{@code layout_marginBottomPercent}
+ * <li>{@code layout_marginStartPercent}
+ * <li>{@code layout_marginEndPercent}
+ * </ul>
+ *
+ * It is not necessary to specify {@code layout_width/height} if you specify {@code
+ * layout_widthPercent.} However, if you want the view to be able to take up more space than what
+ * percentage value permits, you can add {@code layout_width/height="wrap_content"}. In that case
+ * if the percentage size is too small for the View's content, it will be resized using
+ * {@code wrap_content} rule.
+ */
+public class PercentFrameLayout extends FrameLayout {
+ private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this);
+
+ public PercentFrameLayout(Context context) {
+ super(context);
+ }
+
+ public PercentFrameLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public PercentFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (mHelper.handleMeasuredStateTooSmall()) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ mHelper.restoreOriginalParams();
+ }
+
+ public static class LayoutParams extends FrameLayout.LayoutParams
+ implements PercentLayoutHelper.PercentLayoutParams {
+ private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+
+ public LayoutParams(int width, int height, int gravity) {
+ super(width, height, gravity);
+ }
+
+ public LayoutParams(ViewGroup.LayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(MarginLayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(FrameLayout.LayoutParams source) {
+ super((MarginLayoutParams) source);
+ gravity = source.gravity;
+ }
+
+ public LayoutParams(LayoutParams source) {
+ this((FrameLayout.LayoutParams) source);
+ mPercentLayoutInfo = source.mPercentLayoutInfo;
+ }
+
+ @Override
+ public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() {
+ return mPercentLayoutInfo;
+ }
+
+ @Override
+ protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
+ PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);
+ }
+ }
+}
diff --git a/percent/src/android/support/percent/PercentLayoutHelper.java b/percent/src/android/support/percent/PercentLayoutHelper.java
new file mode 100644
index 0000000..216371f
--- /dev/null
+++ b/percent/src/android/support/percent/PercentLayoutHelper.java
@@ -0,0 +1,457 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * 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.percent;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.support.v4.view.MarginLayoutParamsCompat;
+import android.support.v4.view.ViewCompat;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import android.support.percent.R;
+
+/**
+ * Helper for layouts that want to support percentage based dimensions.
+ *
+ * <p>This class collects utility methods that are involved in extracting percentage based dimension
+ * attributes and applying them to ViewGroup's children. If you would like to implement a layout
+ * that supports percentage based dimensions, you need to take several steps:
+ *
+ * <ol>
+ * <li> You need a {@link ViewGroup.LayoutParams} subclass in your ViewGroup that implements
+ * {@link android.support.percent.PercentLayoutHelper.PercentLayoutParams}.
+ * <li> In your {@code LayoutParams(Context c, AttributeSet attrs)} constructor create an instance
+ * of {@link PercentLayoutHelper.PercentLayoutInfo} by calling
+ * {@link PercentLayoutHelper#getPercentLayoutInfo(Context, AttributeSet)}. Return this
+ * object from {@code public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo()}
+ * method that you implemented for {@link android.support.percent.PercentLayoutHelper.PercentLayoutParams} interface.
+ * <li> Override
+ * {@link ViewGroup.LayoutParams#setBaseAttributes(TypedArray, int, int)}
+ * with a single line implementation {@code PercentLayoutHelper.fetchWidthAndHeight(this, a,
+ * widthAttr, heightAttr);}
+ * <li> In your ViewGroup override {@link ViewGroup#generateLayoutParams(AttributeSet)} to return
+ * your LayoutParams.
+ * <li> In your {@link ViewGroup#onMeasure(int, int)} override, you need to implement following
+ * pattern:
+ * <pre class="prettyprint">
+ * protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ * mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
+ * super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ * if (mHelper.handleMeasuredStateTooSmall()) {
+ * super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ * }
+ * }
+ * </pre>
+ * <li>In your {@link ViewGroup#onLayout(boolean, int, int, int, int)} override, you need to
+ * implement following pattern:
+ * <pre class="prettyprint">
+ * protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ * super.onLayout(changed, left, top, right, bottom);
+ * mHelper.restoreOriginalParams();
+ * }
+ * </pre>
+ * </ol>
+ */
+public class PercentLayoutHelper {
+ private static final String TAG = "PercentLayout";
+
+ private final ViewGroup mHost;
+
+ public PercentLayoutHelper(ViewGroup host) {
+ mHost = host;
+ }
+
+ /**
+ * Helper method to be called from {@link ViewGroup.LayoutParams#setBaseAttributes} override
+ * that reads layout_width and layout_height attribute values without throwing an exception if
+ * they aren't present.
+ */
+ public static void fetchWidthAndHeight(ViewGroup.LayoutParams params, TypedArray array,
+ int widthAttr, int heightAttr) {
+ params.width = array.getLayoutDimension(widthAttr, 0);
+ params.height = array.getLayoutDimension(heightAttr, 0);
+ }
+
+ /**
+ * Iterates over children and changes their width and height to one calculated from percentage
+ * values.
+ * @param widthMeasureSpec Width MeasureSpec of the parent ViewGroup.
+ * @param heightMeasureSpec Height MeasureSpec of the parent ViewGroup.
+ */
+ public void adjustChildren(int widthMeasureSpec, int heightMeasureSpec) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "adjustChildren: " + mHost + " widthMeasureSpec: "
+ + View.MeasureSpec.toString(widthMeasureSpec) + " heightMeasureSpec: "
+ + View.MeasureSpec.toString(heightMeasureSpec));
+ }
+ int widthHint = View.MeasureSpec.getSize(widthMeasureSpec);
+ int heightHint = View.MeasureSpec.getSize(heightMeasureSpec);
+ for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
+ View view = mHost.getChildAt(i);
+ ViewGroup.LayoutParams params = view.getLayoutParams();
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "should adjust " + view + " " + params);
+ }
+ if (params instanceof PercentLayoutParams) {
+ PercentLayoutInfo info =
+ ((PercentLayoutParams) params).getPercentLayoutInfo();
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "using " + info);
+ }
+ if (info != null) {
+ if (params instanceof ViewGroup.MarginLayoutParams) {
+ info.fillMarginLayoutParams((ViewGroup.MarginLayoutParams) params,
+ widthHint, heightHint);
+ } else {
+ info.fillLayoutParams(params, widthHint, heightHint);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Constructs a PercentLayoutInfo from attributes associated with a View. Call this method from
+ * {@code LayoutParams(Context c, AttributeSet attrs)} constructor.
+ */
+ public static PercentLayoutInfo getPercentLayoutInfo(Context context,
+ AttributeSet attrs) {
+ PercentLayoutInfo info = null;
+ TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PercentLayout_Layout);
+ float value = array.getFraction(R.styleable.PercentLayout_Layout_layout_widthPercent, 1, 1,
+ -1f);
+ if (value != -1f) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "percent width: " + value);
+ }
+ info = info != null ? info : new PercentLayoutInfo();
+ info.widthPercent = value;
+ }
+ value = array.getFraction(R.styleable.PercentLayout_Layout_layout_heightPercent, 1, 1, -1f);
+ if (value != -1f) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "percent height: " + value);
+ }
+ info = info != null ? info : new PercentLayoutInfo();
+ info.heightPercent = value;
+ }
+ value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginPercent, 1, 1, -1f);
+ if (value != -1f) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "percent margin: " + value);
+ }
+ info = info != null ? info : new PercentLayoutInfo();
+ info.leftMarginPercent = value;
+ info.topMarginPercent = value;
+ info.rightMarginPercent = value;
+ info.bottomMarginPercent = value;
+ }
+ value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginLeftPercent, 1, 1,
+ -1f);
+ if (value != -1f) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "percent left margin: " + value);
+ }
+ info = info != null ? info : new PercentLayoutInfo();
+ info.leftMarginPercent = value;
+ }
+ value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginTopPercent, 1, 1,
+ -1f);
+ if (value != -1f) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "percent top margin: " + value);
+ }
+ info = info != null ? info : new PercentLayoutInfo();
+ info.topMarginPercent = value;
+ }
+ value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginRightPercent, 1, 1,
+ -1f);
+ if (value != -1f) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "percent right margin: " + value);
+ }
+ info = info != null ? info : new PercentLayoutInfo();
+ info.rightMarginPercent = value;
+ }
+ value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginBottomPercent, 1, 1,
+ -1f);
+ if (value != -1f) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "percent bottom margin: " + value);
+ }
+ info = info != null ? info : new PercentLayoutInfo();
+ info.bottomMarginPercent = value;
+ }
+ value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginStartPercent, 1, 1,
+ -1f);
+ if (value != -1f) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "percent start margin: " + value);
+ }
+ info = info != null ? info : new PercentLayoutInfo();
+ info.startMarginPercent = value;
+ }
+ value = array.getFraction(R.styleable.PercentLayout_Layout_layout_marginEndPercent, 1, 1,
+ -1f);
+ if (value != -1f) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "percent end margin: " + value);
+ }
+ info = info != null ? info : new PercentLayoutInfo();
+ info.endMarginPercent = value;
+ }
+ array.recycle();
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "constructed: " + info);
+ }
+ return info;
+ }
+
+ /**
+ * Iterates over children and restores their original dimensions that were changed for
+ * percentage values. Calling this method only makes sense if you previously called
+ * {@link PercentLayoutHelper#adjustChildren(int, int)}.
+ */
+ public void restoreOriginalParams() {
+ for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
+ View view = mHost.getChildAt(i);
+ ViewGroup.LayoutParams params = view.getLayoutParams();
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "should restore " + view + " " + params);
+ }
+ if (params instanceof PercentLayoutParams) {
+ PercentLayoutInfo info =
+ ((PercentLayoutParams) params).getPercentLayoutInfo();
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "using " + info);
+ }
+ if (info != null) {
+ if (params instanceof ViewGroup.MarginLayoutParams) {
+ info.restoreMarginLayoutParams((ViewGroup.MarginLayoutParams) params);
+ } else {
+ info.restoreLayoutParams(params);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Iterates over children and checks if any of them would like to get more space than it
+ * received through the percentage dimension.
+ *
+ * If you are building a layout that supports percentage dimensions you are encouraged to take
+ * advantage of this method. The developer should be able to specify that a child should be
+ * remeasured by adding normal dimension attribute with {@code wrap_content} value. For example
+ * he might specify child's attributes as {@code app:layout_widthPercent="60%p"} and
+ * {@code android:layout_width="wrap_content"}. In this case if the child receives too little
+ * space, it will be remeasured with width set to {@code WRAP_CONTENT}.
+ *
+ * @return True if the measure phase needs to be rerun because one of the children would like
+ * to receive more space.
+ */
+ public boolean handleMeasuredStateTooSmall() {
+ boolean needsSecondMeasure = false;
+ for (int i = 0, N = mHost.getChildCount(); i < N; i++) {
+ View view = mHost.getChildAt(i);
+ ViewGroup.LayoutParams params = view.getLayoutParams();
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "should handle measured state too small " + view + " " + params);
+ }
+ if (params instanceof PercentLayoutParams) {
+ PercentLayoutInfo info =
+ ((PercentLayoutParams) params).getPercentLayoutInfo();
+ if (info != null) {
+ if (shouldHandleMeasuredWidthTooSmall(view, info)) {
+ needsSecondMeasure = true;
+ params.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+ }
+ if (shouldHandleMeasuredHeightTooSmall(view, info)) {
+ needsSecondMeasure = true;
+ params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ }
+ }
+ }
+ }
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "should trigger second measure pass: " + needsSecondMeasure);
+ }
+ return needsSecondMeasure;
+ }
+
+ private static boolean shouldHandleMeasuredWidthTooSmall(View view, PercentLayoutInfo info) {
+ int state = ViewCompat.getMeasuredWidthAndState(view) & ViewCompat.MEASURED_STATE_MASK;
+ return state == ViewCompat.MEASURED_STATE_TOO_SMALL && info.widthPercent >= 0 &&
+ info.mPreservedParams.width == ViewGroup.LayoutParams.WRAP_CONTENT;
+ }
+
+ private static boolean shouldHandleMeasuredHeightTooSmall(View view, PercentLayoutInfo info) {
+ int state = ViewCompat.getMeasuredHeightAndState(view) & ViewCompat.MEASURED_STATE_MASK;
+ return state == ViewCompat.MEASURED_STATE_TOO_SMALL && info.heightPercent >= 0 &&
+ info.mPreservedParams.height == ViewGroup.LayoutParams.WRAP_CONTENT;
+ }
+
+ /**
+ * Container for information about percentage dimensions and margins. It acts as an extension
+ * for {@code LayoutParams}.
+ */
+ public static class PercentLayoutInfo {
+ public float widthPercent;
+
+ public float heightPercent;
+
+ public float leftMarginPercent;
+
+ public float topMarginPercent;
+
+ public float rightMarginPercent;
+
+ public float bottomMarginPercent;
+
+ public float startMarginPercent;
+
+ public float endMarginPercent;
+
+ /* package */ final ViewGroup.MarginLayoutParams mPreservedParams;
+
+ public PercentLayoutInfo() {
+ widthPercent = -1f;
+ heightPercent = -1f;
+ leftMarginPercent = -1f;
+ topMarginPercent = -1f;
+ rightMarginPercent = -1f;
+ bottomMarginPercent = -1f;
+ startMarginPercent = -1f;
+ endMarginPercent = -1f;
+ mPreservedParams = new ViewGroup.MarginLayoutParams(0, 0);
+ }
+
+ /**
+ * Fills {@code ViewGroup.LayoutParams} dimensions based on percentage values.
+ */
+ public void fillLayoutParams(ViewGroup.LayoutParams params, int widthHint,
+ int heightHint) {
+ // Preserve the original layout params, so we can restore them after the measure step.
+ mPreservedParams.width = params.width;
+ mPreservedParams.height = params.height;
+
+ if (widthPercent >= 0) {
+ params.width = (int) (widthHint * widthPercent);
+ }
+ if (heightPercent >= 0) {
+ params.height = (int) (heightHint * heightPercent);
+ }
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "after fillLayoutParams: (" + params.width + ", " + params.height + ")");
+ }
+ }
+
+ /**
+ * Fills {@code ViewGroup.MarginLayoutParams} dimensions and margins based on percentage
+ * values.
+ */
+ public void fillMarginLayoutParams(ViewGroup.MarginLayoutParams params, int widthHint,
+ int heightHint) {
+ fillLayoutParams(params, widthHint, heightHint);
+
+ // Preserver the original margins, so we can restore them after the measure step.
+ mPreservedParams.leftMargin = params.leftMargin;
+ mPreservedParams.topMargin = params.topMargin;
+ mPreservedParams.rightMargin = params.rightMargin;
+ mPreservedParams.bottomMargin = params.bottomMargin;
+ MarginLayoutParamsCompat.setMarginStart(mPreservedParams,
+ MarginLayoutParamsCompat.getMarginStart(params));
+ MarginLayoutParamsCompat.setMarginEnd(mPreservedParams,
+ MarginLayoutParamsCompat.getMarginEnd(params));
+
+ if (leftMarginPercent >= 0) {
+ params.leftMargin = (int) (widthHint * leftMarginPercent);
+ }
+ if (topMarginPercent >= 0) {
+ params.topMargin = (int) (heightHint * topMarginPercent);
+ }
+ if (rightMarginPercent >= 0) {
+ params.rightMargin = (int) (widthHint * rightMarginPercent);
+ }
+ if (bottomMarginPercent >= 0) {
+ params.bottomMargin = (int) (heightHint * bottomMarginPercent);
+ }
+ if (startMarginPercent >= 0) {
+ MarginLayoutParamsCompat.setMarginStart(params,
+ (int) (widthHint * startMarginPercent));
+ }
+ if (endMarginPercent >= 0) {
+ MarginLayoutParamsCompat.setMarginEnd(params,
+ (int) (widthHint * endMarginPercent));
+ }
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "after fillMarginLayoutParams: (" + params.width + ", " + params.height
+ + ")");
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format("PercentLayoutInformation width: %f height %f, margins (%f, %f, "
+ + " %f, %f, %f, %f)", widthPercent, heightPercent, leftMarginPercent,
+ topMarginPercent, rightMarginPercent, bottomMarginPercent, startMarginPercent,
+ endMarginPercent);
+
+ }
+
+ /**
+ * Restores original dimensions and margins after they were changed for percentage based
+ * values. Calling this method only makes sense if you previously called
+ * {@link PercentLayoutHelper.PercentLayoutInfo#fillMarginLayoutParams}.
+ */
+ public void restoreMarginLayoutParams(ViewGroup.MarginLayoutParams params) {
+ restoreLayoutParams(params);
+ params.leftMargin = mPreservedParams.leftMargin;
+ params.topMargin = mPreservedParams.topMargin;
+ params.rightMargin = mPreservedParams.rightMargin;
+ params.bottomMargin = mPreservedParams.bottomMargin;
+ MarginLayoutParamsCompat.setMarginStart(params,
+ MarginLayoutParamsCompat.getMarginStart(mPreservedParams));
+ MarginLayoutParamsCompat.setMarginEnd(params,
+ MarginLayoutParamsCompat.getMarginEnd(mPreservedParams));
+ }
+
+ /**
+ * Restores original dimensions after they were changed for percentage based values. Calling
+ * this method only makes sense if you previously called
+ * {@link PercentLayoutHelper.PercentLayoutInfo#fillLayoutParams}.
+ */
+ public void restoreLayoutParams(ViewGroup.LayoutParams params) {
+ params.width = mPreservedParams.width;
+ params.height = mPreservedParams.height;
+ }
+ }
+
+ /**
+ * If a layout wants to support percentage based dimensions and use this helper class, its
+ * {@code LayoutParams} subclass must implement this interface.
+ *
+ * Your {@code LayoutParams} subclass should contain an instance of {@code PercentLayoutInfo}
+ * and the implementation of this interface should be a simple accessor.
+ */
+ public interface PercentLayoutParams {
+ PercentLayoutInfo getPercentLayoutInfo();
+ }
+}
diff --git a/percent/src/android/support/percent/PercentRelativeLayout.java b/percent/src/android/support/percent/PercentRelativeLayout.java
new file mode 100644
index 0000000..1a06b8d
--- /dev/null
+++ b/percent/src/android/support/percent/PercentRelativeLayout.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * 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.percent;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.ViewGroup;
+import android.widget.RelativeLayout;
+
+/**
+ * Subclass of {@link android.widget.RelativeLayout} that supports percentage based dimensions and
+ * margins.
+ *
+ * You can specify dimension or a margin of child by using attributes with "Percent" suffix. Follow
+ * this example:
+ *
+ * <pre class="prettyprint">
+ * <android.support.percent.PercentRelativeLayout
+ * xmlns:android="http://schemas.android.com/apk/res/android"
+ * xmlns:app="http://schemas.android.com/apk/res-auto"
+ * android:layout_width="match_parent"
+ * android:layout_height="match_parent"/>
+ * <ImageView
+ * app:layout_widthPercent="50%"
+ * app:layout_heightPercent="50%"
+ * app:layout_marginTopPercent="25%"
+ * app:layout_marginLeftPercent="25%"/>
+ * </android.support.percent.PercentFrameLayout/>
+ * </pre>
+ *
+ * The attributes that you can use are:
+ * <ul>
+ * <li>{@code layout_widthPercent}
+ * <li>{@code layout_heightPercent}
+ * <li>{@code layout_marginPercent}
+ * <li>{@code layout_marginLeftPercent}
+ * <li>{@code layout_marginTopPercent}
+ * <li>{@code layout_marginRightPercent}
+ * <li>{@code layout_marginBottomPercent}
+ * <li>{@code layout_marginStartPercent}
+ * <li>{@code layout_marginEndPercent}
+ * </ul>
+ *
+ * It is not necessary to specify {@code layout_width/height} if you specify {@code
+ * layout_widthPercent.} However, if you want the view to be able to take up more space than what
+ * percentage value permits, you can add {@code layout_width/height="wrap_content"}. In that case
+ * if the percentage size is too small for the View's content, it will be resized using
+ * {@code wrap_content} rule.
+ */
+public class PercentRelativeLayout extends RelativeLayout {
+ private final PercentLayoutHelper mHelper = new PercentLayoutHelper(this);
+
+ public PercentRelativeLayout(Context context) {
+ super(context);
+ }
+
+ public PercentRelativeLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public PercentRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ mHelper.adjustChildren(widthMeasureSpec, heightMeasureSpec);
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (mHelper.handleMeasuredStateTooSmall()) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ mHelper.restoreOriginalParams();
+ }
+
+ public static class LayoutParams extends RelativeLayout.LayoutParams
+ implements PercentLayoutHelper.PercentLayoutParams {
+ private PercentLayoutHelper.PercentLayoutInfo mPercentLayoutInfo;
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ mPercentLayoutInfo = PercentLayoutHelper.getPercentLayoutInfo(c, attrs);
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+
+ public LayoutParams(ViewGroup.LayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(MarginLayoutParams source) {
+ super(source);
+ }
+
+ @Override
+ public PercentLayoutHelper.PercentLayoutInfo getPercentLayoutInfo() {
+ return mPercentLayoutInfo;
+ }
+
+ @Override
+ protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
+ PercentLayoutHelper.fetchWidthAndHeight(this, a, widthAttr, heightAttr);
+ }
+ }
+}
diff --git a/settings.gradle b/settings.gradle
index c4fdee2..85c9ae1 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -39,3 +39,6 @@
include ':support-design'
project(':support-design').projectDir = new File(rootDir, 'design')
+
+include ':support-percent'
+project(':support-percent').projectDir = new File(rootDir, 'percent')
diff --git a/v4/api/current.txt b/v4/api/current.txt
index 2bc5d17..274a7b3 100644
--- a/v4/api/current.txt
+++ b/v4/api/current.txt
@@ -2449,9 +2449,11 @@
public class ViewPager extends android.view.ViewGroup {
ctor public ViewPager(android.content.Context);
ctor public ViewPager(android.content.Context, android.util.AttributeSet);
+ method public void addOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
method public boolean arrowScroll(int);
method public boolean beginFakeDrag();
method protected boolean canScroll(android.view.View, boolean, int, int, int);
+ method public void clearOnPageChangeListeners();
method public void endFakeDrag();
method public boolean executeKeyEvent(android.view.KeyEvent);
method public void fakeDragBy(float);
@@ -2464,11 +2466,12 @@
method protected void onPageScrolled(int, float, int);
method public void onRestoreInstanceState(android.os.Parcelable);
method public android.os.Parcelable onSaveInstanceState();
+ method public void removeOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
method public void setAdapter(android.support.v4.view.PagerAdapter);
method public void setCurrentItem(int);
method public void setCurrentItem(int, boolean);
method public void setOffscreenPageLimit(int);
- method public void setOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
+ method public deprecated void setOnPageChangeListener(android.support.v4.view.ViewPager.OnPageChangeListener);
method public void setPageMargin(int);
method public void setPageMarginDrawable(android.graphics.drawable.Drawable);
method public void setPageMarginDrawable(int);
diff --git a/v4/java/android/support/v4/view/ViewPager.java b/v4/java/android/support/v4/view/ViewPager.java
index dc20cb6..f19104a 100644
--- a/v4/java/android/support/v4/view/ViewPager.java
+++ b/v4/java/android/support/v4/view/ViewPager.java
@@ -56,6 +56,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.List;
/**
* Layout manager that allows the user to flip left and right
@@ -214,6 +215,7 @@
private boolean mCalledSuper;
private int mDecorChildCount;
+ private List<OnPageChangeListener> mOnPageChangeListeners;
private OnPageChangeListener mOnPageChangeListener;
private OnPageChangeListener mInternalPageChangeListener;
private OnAdapterChangeListener mAdapterChangeListener;
@@ -399,9 +401,7 @@
// PageTransformers can do complex things that benefit from hardware layers.
enableLayers(newState != SCROLL_STATE_IDLE);
}
- if (mOnPageChangeListener != null) {
- mOnPageChangeListener.onPageScrollStateChanged(newState);
- }
+ dispatchOnScrollStateChanged(newState);
}
/**
@@ -544,11 +544,8 @@
// We don't have any idea how big we are yet and shouldn't have any pages either.
// Just set things up and let the pending layout handle things.
mCurItem = item;
- if (dispatchSelected && mOnPageChangeListener != null) {
- mOnPageChangeListener.onPageSelected(item);
- }
- if (dispatchSelected && mInternalPageChangeListener != null) {
- mInternalPageChangeListener.onPageSelected(item);
+ if (dispatchSelected) {
+ dispatchOnPageSelected(item);
}
requestLayout();
} else {
@@ -568,18 +565,12 @@
}
if (smoothScroll) {
smoothScrollTo(destX, 0, velocity);
- if (dispatchSelected && mOnPageChangeListener != null) {
- mOnPageChangeListener.onPageSelected(item);
- }
- if (dispatchSelected && mInternalPageChangeListener != null) {
- mInternalPageChangeListener.onPageSelected(item);
+ if (dispatchSelected) {
+ dispatchOnPageSelected(item);
}
} else {
- if (dispatchSelected && mOnPageChangeListener != null) {
- mOnPageChangeListener.onPageSelected(item);
- }
- if (dispatchSelected && mInternalPageChangeListener != null) {
- mInternalPageChangeListener.onPageSelected(item);
+ if (dispatchSelected) {
+ dispatchOnPageSelected(item);
}
completeScroll(false);
scrollTo(destX, 0);
@@ -592,12 +583,54 @@
* scrolled. See {@link OnPageChangeListener}.
*
* @param listener Listener to set
+ *
+ * @deprecated Use {@link #addOnPageChangeListener(OnPageChangeListener)}
+ * and {@link #removeOnPageChangeListener(OnPageChangeListener)} instead.
*/
+ @Deprecated
public void setOnPageChangeListener(OnPageChangeListener listener) {
mOnPageChangeListener = listener;
}
/**
+ * Add a listener that will be invoked whenever the page changes or is incrementally
+ * scrolled. See {@link OnPageChangeListener}.
+ *
+ * <p>Components that add a listener should take care to remove it when finished.
+ * Other components that take ownership of a view may call {@link #clearOnPageChangeListeners()}
+ * to remove all attached listeners.</p>
+ *
+ * @param listener listener to add
+ */
+ public void addOnPageChangeListener(OnPageChangeListener listener) {
+ if (mOnPageChangeListeners == null) {
+ mOnPageChangeListeners = new ArrayList<>();
+ }
+ mOnPageChangeListeners.add(listener);
+ }
+
+ /**
+ * Remove a listener that was previously added via
+ * {@link #addOnPageChangeListener(OnPageChangeListener)}.
+ *
+ * @param listener listener to remove
+ */
+ public void removeOnPageChangeListener(OnPageChangeListener listener) {
+ if (mOnPageChangeListeners != null) {
+ mOnPageChangeListeners.remove(listener);
+ }
+ }
+
+ /**
+ * Remove all listeners that are notified of any changes in scroll state or position.
+ */
+ public void clearOnPageChangeListeners() {
+ if (mOnPageChangeListeners != null) {
+ mOnPageChangeListeners.clear();
+ }
+ }
+
+ /**
* Set a {@link PageTransformer} that will be called for each attached page whenever
* the scroll position is changed. This allows the application to apply custom property
* transformations to each page, overriding the default sliding look and feel.
@@ -1714,12 +1747,7 @@
}
}
- if (mOnPageChangeListener != null) {
- mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels);
- }
- if (mInternalPageChangeListener != null) {
- mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);
- }
+ dispatchOnPageScrolled(position, offset, offsetPixels);
if (mPageTransformer != null) {
final int scrollX = getScrollX();
@@ -1738,6 +1766,57 @@
mCalledSuper = true;
}
+ private void dispatchOnPageScrolled(int position, float offset, int offsetPixels) {
+ if (mOnPageChangeListener != null) {
+ mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels);
+ }
+ if (mOnPageChangeListeners != null) {
+ for (int i = 0, z = mOnPageChangeListeners.size(); i < z; i++) {
+ OnPageChangeListener listener = mOnPageChangeListeners.get(i);
+ if (listener != null) {
+ listener.onPageScrolled(position, offset, offsetPixels);
+ }
+ }
+ }
+ if (mInternalPageChangeListener != null) {
+ mInternalPageChangeListener.onPageScrolled(position, offset, offsetPixels);
+ }
+ }
+
+ private void dispatchOnPageSelected(int position) {
+ if (mOnPageChangeListener != null) {
+ mOnPageChangeListener.onPageSelected(position);
+ }
+ if (mOnPageChangeListeners != null) {
+ for (int i = 0, z = mOnPageChangeListeners.size(); i < z; i++) {
+ OnPageChangeListener listener = mOnPageChangeListeners.get(i);
+ if (listener != null) {
+ listener.onPageSelected(position);
+ }
+ }
+ }
+ if (mInternalPageChangeListener != null) {
+ mInternalPageChangeListener.onPageSelected(position);
+ }
+ }
+
+ private void dispatchOnScrollStateChanged(int state) {
+ if (mOnPageChangeListener != null) {
+ mOnPageChangeListener.onPageScrollStateChanged(state);
+ }
+ if (mOnPageChangeListeners != null) {
+ for (int i = 0, z = mOnPageChangeListeners.size(); i < z; i++) {
+ OnPageChangeListener listener = mOnPageChangeListeners.get(i);
+ if (listener != null) {
+ listener.onPageScrollStateChanged(state);
+ }
+ }
+ }
+ if (mInternalPageChangeListener != null) {
+ mInternalPageChangeListener.onPageScrollStateChanged(state);
+ }
+ }
+
private void completeScroll(boolean postEvents) {
boolean needPopulate = mScrollState == SCROLL_STATE_SETTLING;
if (needPopulate) {
diff --git a/v4/java/android/support/v4/widget/MaterialProgressDrawable.java b/v4/java/android/support/v4/widget/MaterialProgressDrawable.java
index c054404..db46186 100644
--- a/v4/java/android/support/v4/widget/MaterialProgressDrawable.java
+++ b/v4/java/android/support/v4/widget/MaterialProgressDrawable.java
@@ -492,7 +492,7 @@
private int mArrowWidth;
private int mArrowHeight;
private int mAlpha;
- private final Paint mCirclePaint = new Paint();
+ private final Paint mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private int mBackgroundColor;
private int mCurrentColor;
diff --git a/v7/appcompat/api/current.txt b/v7/appcompat/api/current.txt
index e88c4d7..4973452 100644
--- a/v7/appcompat/api/current.txt
+++ b/v7/appcompat/api/current.txt
@@ -374,6 +374,7 @@
field public static int backgroundTint;
field public static int backgroundTintMode;
field public static int barSize;
+ field public static int borderlessButtonStyle;
field public static int buttonBarButtonStyle;
field public static int buttonBarNegativeButtonStyle;
field public static int buttonBarNeutralButtonStyle;
@@ -742,7 +743,7 @@
field public static int abc_switch_track_mtrl_alpha;
field public static int abc_tab_indicator_material;
field public static int abc_tab_indicator_mtrl_alpha;
- field public static int abc_text_cursor_mtrl_alpha;
+ field public static int abc_text_cursor_material;
field public static int abc_textfield_activated_mtrl_alpha;
field public static int abc_textfield_default_mtrl_alpha;
field public static int abc_textfield_search_activated_mtrl_alpha;
@@ -995,6 +996,8 @@
field public static int Base_Theme_AppCompat_Light_Dialog_MinWidth;
field public static int Base_V11_Theme_AppCompat_Dialog;
field public static int Base_V11_Theme_AppCompat_Light_Dialog;
+ field public static int Base_V12_Widget_AppCompat_AutoCompleteTextView;
+ field public static int Base_V12_Widget_AppCompat_EditText;
field public static int Base_V21_Theme_AppCompat;
field public static int Base_V21_Theme_AppCompat_Dialog;
field public static int Base_V21_Theme_AppCompat_Light;
@@ -1003,6 +1006,8 @@
field public static int Base_V7_Theme_AppCompat_Dialog;
field public static int Base_V7_Theme_AppCompat_Light;
field public static int Base_V7_Theme_AppCompat_Light_Dialog;
+ field public static int Base_V7_Widget_AppCompat_AutoCompleteTextView;
+ field public static int Base_V7_Widget_AppCompat_EditText;
field public static int Base_Widget_AppCompat_ActionBar;
field public static int Base_Widget_AppCompat_ActionBar_Solid;
field public static int Base_Widget_AppCompat_ActionBar_TabBar;
@@ -1060,8 +1065,6 @@
field public static int Platform_ThemeOverlay_AppCompat_Light;
field public static int Platform_V11_AppCompat;
field public static int Platform_V11_AppCompat_Light;
- field public static int Platform_V12_AppCompat;
- field public static int Platform_V12_AppCompat_Light;
field public static int Platform_V14_AppCompat;
field public static int Platform_V14_AppCompat_Light;
field public static int RtlOverlay_DialogWindowTitle_AppCompat;
@@ -1423,6 +1426,7 @@
field public static int Theme_android_windowAnimationStyle;
field public static int Theme_android_windowIsFloating;
field public static int Theme_autoCompleteTextViewStyle;
+ field public static int Theme_borderlessButtonStyle;
field public static int Theme_buttonBarButtonStyle;
field public static int Theme_buttonBarNegativeButtonStyle;
field public static int Theme_buttonBarNeutralButtonStyle;
diff --git a/v7/appcompat/res-public/values/public_attrs.xml b/v7/appcompat/res-public/values/public_attrs.xml
index d70b9d1..3ded592 100644
--- a/v7/appcompat/res-public/values/public_attrs.xml
+++ b/v7/appcompat/res-public/values/public_attrs.xml
@@ -57,6 +57,7 @@
<public type="attr" name="backgroundTint"/>
<public type="attr" name="backgroundTintMode"/>
<public type="attr" name="barSize"/>
+ <public type="attr" name="borderlessButtonStyle"/>
<public type="attr" name="buttonBarButtonStyle"/>
<public type="attr" name="buttonBarNegativeButtonStyle"/>
<public type="attr" name="buttonBarNeutralButtonStyle"/>
diff --git a/v7/appcompat/res/drawable-hdpi/abc_text_cursor_mtrl_alpha.9.png b/v7/appcompat/res/drawable-hdpi/abc_text_cursor_mtrl_alpha.9.png
deleted file mode 100644
index 5e0bf84..0000000
--- a/v7/appcompat/res/drawable-hdpi/abc_text_cursor_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-mdpi/abc_text_cursor_mtrl_alpha.9.png b/v7/appcompat/res/drawable-mdpi/abc_text_cursor_mtrl_alpha.9.png
deleted file mode 100644
index 36348a8..0000000
--- a/v7/appcompat/res/drawable-mdpi/abc_text_cursor_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xhdpi/abc_text_cursor_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xhdpi/abc_text_cursor_mtrl_alpha.9.png
deleted file mode 100644
index 666b10a..0000000
--- a/v7/appcompat/res/drawable-xhdpi/abc_text_cursor_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable-xxhdpi/abc_text_cursor_mtrl_alpha.9.png b/v7/appcompat/res/drawable-xxhdpi/abc_text_cursor_mtrl_alpha.9.png
deleted file mode 100644
index 08ee2b4..0000000
--- a/v7/appcompat/res/drawable-xxhdpi/abc_text_cursor_mtrl_alpha.9.png
+++ /dev/null
Binary files differ
diff --git a/v7/appcompat/res/drawable/abc_text_cursor_material.xml b/v7/appcompat/res/drawable/abc_text_cursor_material.xml
new file mode 100644
index 0000000..885670c
--- /dev/null
+++ b/v7/appcompat/res/drawable/abc_text_cursor_material.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <size android:height="2dp"
+ android:width="2dp"/>
+ <solid android:color="@android:color/white"/>
+</shape>
\ No newline at end of file
diff --git a/v7/appcompat/res/values-v12/themes_base.xml b/v7/appcompat/res/values-v12/styles_base.xml
similarity index 63%
rename from v7/appcompat/res/values-v12/themes_base.xml
rename to v7/appcompat/res/values-v12/styles_base.xml
index c912434..f7965a4 100644
--- a/v7/appcompat/res/values-v12/themes_base.xml
+++ b/v7/appcompat/res/values-v12/styles_base.xml
@@ -16,15 +16,16 @@
<resources>
- <style name="Platform.AppCompat" parent="Platform.V12.AppCompat" />
- <style name="Platform.AppCompat.Light" parent="Platform.V12.AppCompat.Light" />
+ <style name="Base.Widget.AppCompat.EditText" parent="Base.V12.Widget.AppCompat.EditText" />
- <style name="Platform.V12.AppCompat" parent="Platform.V11.AppCompat">
- <item name="android:textCursorDrawable">@drawable/abc_text_cursor_mtrl_alpha</item>
+ <style name="Base.V12.Widget.AppCompat.EditText" parent="Base.V7.Widget.AppCompat.EditText">
+ <item name="android:textCursorDrawable">@drawable/abc_text_cursor_material</item>
</style>
- <style name="Platform.V12.AppCompat.Light" parent="Platform.V11.AppCompat.Light">
- <item name="android:textCursorDrawable">@drawable/abc_text_cursor_mtrl_alpha</item>
+ <style name="Base.Widget.AppCompat.AutoCompleteTextView" parent="Base.V12.Widget.AppCompat.AutoCompleteTextView" />
+
+ <style name="Base.V12.Widget.AppCompat.AutoCompleteTextView" parent="Base.V7.Widget.AppCompat.AutoCompleteTextView">
+ <item name="android:textCursorDrawable">@drawable/abc_text_cursor_material</item>
</style>
</resources>
diff --git a/v7/appcompat/res/values-v14/themes_base.xml b/v7/appcompat/res/values-v14/themes_base.xml
index 5fdc73c..97b7aaa 100644
--- a/v7/appcompat/res/values-v14/themes_base.xml
+++ b/v7/appcompat/res/values-v14/themes_base.xml
@@ -19,14 +19,14 @@
<style name="Platform.AppCompat" parent="Platform.V14.AppCompat" />
<style name="Platform.AppCompat.Light" parent="Platform.V14.AppCompat.Light" />
- <style name="Platform.V14.AppCompat" parent="Platform.V12.AppCompat">
+ <style name="Platform.V14.AppCompat" parent="Platform.V11.AppCompat">
<item name="android:actionModeSelectAllDrawable">?actionModeSelectAllDrawable</item>
<item name="android:listPreferredItemPaddingLeft">@dimen/abc_list_item_padding_horizontal_material</item>
<item name="android:listPreferredItemPaddingRight">@dimen/abc_list_item_padding_horizontal_material</item>
</style>
- <style name="Platform.V14.AppCompat.Light" parent="Platform.V12.AppCompat.Light">
+ <style name="Platform.V14.AppCompat.Light" parent="Platform.V11.AppCompat.Light">
<item name="android:actionModeSelectAllDrawable">?actionModeSelectAllDrawable</item>
<item name="android:listPreferredItemPaddingLeft">@dimen/abc_list_item_padding_horizontal_material</item>
diff --git a/v7/appcompat/res/values-v21/themes_base.xml b/v7/appcompat/res/values-v21/themes_base.xml
index a8c3df4..06b33b1 100644
--- a/v7/appcompat/res/values-v21/themes_base.xml
+++ b/v7/appcompat/res/values-v21/themes_base.xml
@@ -64,6 +64,7 @@
<!-- General view attributes -->
<item name="selectableItemBackground">?android:attr/selectableItemBackground</item>
<item name="selectableItemBackgroundBorderless">?android:attr/selectableItemBackgroundBorderless</item>
+ <item name="borderlessButtonStyle">?android:borderlessButtonStyle</item>
<item name="dividerHorizontal">?android:attr/dividerHorizontal</item>
<item name="dividerVertical">?android:attr/dividerVertical</item>
<item name="editTextBackground">?android:attr/editTextBackground</item>
@@ -113,6 +114,7 @@
<!-- General view attributes -->
<item name="selectableItemBackground">?android:attr/selectableItemBackground</item>
<item name="selectableItemBackgroundBorderless">?android:attr/selectableItemBackgroundBorderless</item>
+ <item name="borderlessButtonStyle">?android:borderlessButtonStyle</item>
<item name="dividerHorizontal">?android:attr/dividerHorizontal</item>
<item name="dividerVertical">?android:attr/dividerVertical</item>
<item name="editTextBackground">?android:attr/editTextBackground</item>
diff --git a/v7/appcompat/res/values/attrs.xml b/v7/appcompat/res/values/attrs.xml
index 17286fb..6a6870c 100644
--- a/v7/appcompat/res/values/attrs.xml
+++ b/v7/appcompat/res/values/attrs.xml
@@ -215,6 +215,8 @@
<attr name="selectableItemBackground" format="reference"/>
<!-- Background drawable for borderless standalone items that need focus/pressed states. -->
<attr name="selectableItemBackgroundBorderless" format="reference" />
+ <!-- Style for buttons without an explicit border, often used in groups. -->
+ <attr name="borderlessButtonStyle" format="reference" />
<!-- A drawable that may be used as a vertical divider between visual elements. -->
<attr name="dividerVertical" format="reference"/>
<!-- A drawable that may be used as a horizontal divider between visual elements. -->
diff --git a/v7/appcompat/res/values/styles_base.xml b/v7/appcompat/res/values/styles_base.xml
index f445e73..0feb530 100644
--- a/v7/appcompat/res/values/styles_base.xml
+++ b/v7/appcompat/res/values/styles_base.xml
@@ -287,7 +287,9 @@
<item name="android:textColor">?android:textColorSecondary</item>
</style>
- <style name="Base.Widget.AppCompat.AutoCompleteTextView" parent="android:Widget.AutoCompleteTextView">
+ <style name="Base.Widget.AppCompat.AutoCompleteTextView" parent="Base.V7.Widget.AppCompat.AutoCompleteTextView" />
+
+ <style name="Base.V7.Widget.AppCompat.AutoCompleteTextView" parent="android:Widget.AutoCompleteTextView">
<item name="android:dropDownSelector">?attr/listChoiceBackgroundIndicator</item>
<item name="android:popupBackground">@drawable/abc_popup_background_mtrl_mult</item>
<item name="android:background">?attr/editTextBackground</item>
@@ -351,11 +353,14 @@
<item name="defaultQueryHint">@string/abc_search_hint</item>
</style>
- <style name="Base.Widget.AppCompat.EditText" parent="android:Widget.EditText">
+ <style name="Base.Widget.AppCompat.EditText" parent="Base.V7.Widget.AppCompat.EditText" />
+
+ <style name="Base.V7.Widget.AppCompat.EditText" parent="android:Widget.EditText">
<item name="android:background">?attr/editTextBackground</item>
<item name="android:textColor">?attr/editTextColor</item>
<item name="android:textAppearance">?android:attr/textAppearanceMediumInverse</item>
</style>
+
<!-- contains values used in all dpis -->
<style name="Base.Widget.AppCompat.DrawerArrowToggle.Common" parent="">
<item name="color">?android:attr/textColorSecondary</item>
diff --git a/v7/appcompat/res/values/themes_base.xml b/v7/appcompat/res/values/themes_base.xml
index 0f3732e..3e2c841 100644
--- a/v7/appcompat/res/values/themes_base.xml
+++ b/v7/appcompat/res/values/themes_base.xml
@@ -119,6 +119,7 @@
<item name="selectableItemBackground">@drawable/abc_item_background_holo_dark</item>
<item name="selectableItemBackgroundBorderless">?attr/selectableItemBackground</item>
+ <item name="borderlessButtonStyle">@style/Widget.AppCompat.Button.Borderless</item>
<item name="homeAsUpIndicator">@drawable/abc_ic_ab_back_mtrl_am_alpha</item>
<item name="dividerVertical">@drawable/abc_list_divider_mtrl_alpha</item>
@@ -266,6 +267,7 @@
<item name="selectableItemBackground">@drawable/abc_item_background_holo_light</item>
<item name="selectableItemBackgroundBorderless">?attr/selectableItemBackground</item>
+ <item name="borderlessButtonStyle">@style/Widget.AppCompat.Button.Borderless</item>
<item name="homeAsUpIndicator">@drawable/abc_ic_ab_back_mtrl_am_alpha</item>
<item name="dividerVertical">@drawable/abc_list_divider_mtrl_alpha</item>
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/ActivityChooserModel.java b/v7/appcompat/src/android/support/v7/internal/widget/ActivityChooserModel.java
index d97f8e7..1cdbbbd 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/ActivityChooserModel.java
+++ b/v7/appcompat/src/android/support/v7/internal/widget/ActivityChooserModel.java
@@ -37,6 +37,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -972,7 +973,7 @@
}
try {
XmlPullParser parser = Xml.newPullParser();
- parser.setInput(fis, null);
+ parser.setInput(fis, StandardCharsets.UTF_8.name());
int type = XmlPullParser.START_DOCUMENT;
while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
@@ -1055,7 +1056,7 @@
try {
serializer.setOutput(fos, null);
- serializer.startDocument("UTF-8", true);
+ serializer.startDocument(StandardCharsets.UTF_8.name(), true);
serializer.startTag(null, TAG_HISTORICAL_RECORDS);
final int recordCount = historicalRecords.size();
diff --git a/v7/appcompat/src/android/support/v7/internal/widget/TintManager.java b/v7/appcompat/src/android/support/v7/internal/widget/TintManager.java
index 1e06c4d..fadfdcb 100644
--- a/v7/appcompat/src/android/support/v7/internal/widget/TintManager.java
+++ b/v7/appcompat/src/android/support/v7/internal/widget/TintManager.java
@@ -90,7 +90,7 @@
R.drawable.abc_textfield_activated_mtrl_alpha,
R.drawable.abc_textfield_search_activated_mtrl_alpha,
R.drawable.abc_cab_background_top_mtrl_alpha,
- R.drawable.abc_text_cursor_mtrl_alpha
+ R.drawable.abc_text_cursor_material
};
/**
@@ -112,8 +112,6 @@
R.drawable.abc_tab_indicator_material,
R.drawable.abc_textfield_search_material,
R.drawable.abc_spinner_mtrl_am_alpha,
- R.drawable.abc_btn_check_material,
- R.drawable.abc_btn_radio_material,
R.drawable.abc_spinner_textfield_background_material,
R.drawable.abc_ratingbar_full_material,
R.drawable.abc_switch_track_mtrl_alpha,
@@ -122,6 +120,16 @@
R.drawable.abc_btn_borderless_material
};
+ /**
+ * Drawables which should be tinted using a state list containing values of
+ * {@code R.attr.colorControlNormal} and {@code R.attr.colorControlActivated} for the checked
+ * state.
+ */
+ private static final int[] TINT_CHECKABLE_BUTTON_LIST = {
+ R.drawable.abc_btn_check_material,
+ R.drawable.abc_btn_radio_material
+ };
+
private final WeakReference<Context> mContextRef;
private SparseArray<ColorStateList> mTintLists;
private ColorStateList mDefaultColorStateList;
@@ -255,6 +263,7 @@
arrayContains(COLORFILTER_COLOR_CONTROL_ACTIVATED, drawableId) ||
arrayContains(TINT_COLOR_CONTROL_STATE_LIST, drawableId) ||
arrayContains(COLORFILTER_COLOR_BACKGROUND_MULTIPLY, drawableId) ||
+ arrayContains(TINT_CHECKABLE_BUTTON_LIST, drawableId) ||
drawableId == R.drawable.abc_cab_background_top_material;
}
@@ -293,6 +302,8 @@
tint = getThemeAttrColorStateList(context, R.attr.colorControlNormal);
} else if (arrayContains(TINT_COLOR_CONTROL_STATE_LIST, resId)) {
tint = getDefaultColorStateList(context);
+ } else if (arrayContains(TINT_CHECKABLE_BUTTON_LIST, resId)) {
+ tint = createCheckableButtonColorStateList(context);
}
if (tint != null) {
@@ -357,6 +368,28 @@
return mDefaultColorStateList;
}
+ private ColorStateList createCheckableButtonColorStateList(Context context) {
+ final int[][] states = new int[3][];
+ final int[] colors = new int[3];
+ int i = 0;
+
+ // Disabled state
+ states[i] = ThemeUtils.DISABLED_STATE_SET;
+ colors[i] = getDisabledThemeAttrColor(context, R.attr.colorControlNormal);
+ i++;
+
+ states[i] = ThemeUtils.CHECKED_STATE_SET;
+ colors[i] = getThemeAttrColor(context, R.attr.colorControlActivated);
+ i++;
+
+ // Default enabled state
+ states[i] = ThemeUtils.EMPTY_STATE_SET;
+ colors[i] = getThemeAttrColor(context, R.attr.colorControlNormal);
+ i++;
+
+ return new ColorStateList(states, colors);
+ }
+
private ColorStateList createSwitchTrackColorStateList(Context context) {
final int[][] states = new int[3][];
final int[] colors = new int[3];
diff --git a/v7/mediarouter/src/android/support/v7/media/MediaRouter.java b/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
index 5bb998e..c84ebc7 100644
--- a/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
+++ b/v7/mediarouter/src/android/support/v7/media/MediaRouter.java
@@ -2197,23 +2197,19 @@
public void setMediaSessionCompat(final MediaSessionCompat session) {
mCompatSession = session;
- if (session == null) {
- if (mRccMediaSession != null) {
- removeRemoteControlClient(mRccMediaSession.getRemoteControlClient());
- mRccMediaSession.removeOnActiveChangeListener(mSessionActiveListener);
- }
- }
if (android.os.Build.VERSION.SDK_INT >= 21) {
- setMediaSession(session.getMediaSession());
+ setMediaSession(session != null ? session.getMediaSession() : null);
} else if (android.os.Build.VERSION.SDK_INT >= 14) {
if (mRccMediaSession != null) {
removeRemoteControlClient(mRccMediaSession.getRemoteControlClient());
mRccMediaSession.removeOnActiveChangeListener(mSessionActiveListener);
}
mRccMediaSession = session;
- session.addOnActiveChangeListener(mSessionActiveListener);
- if (session.isActive()) {
- addRemoteControlClient(session.getRemoteControlClient());
+ if (session != null) {
+ session.addOnActiveChangeListener(mSessionActiveListener);
+ if (session.isActive()) {
+ addRemoteControlClient(session.getRemoteControlClient());
+ }
}
}
}
diff --git a/v7/palette/src/main/java/android/support/v7/graphics/Palette.java b/v7/palette/src/main/java/android/support/v7/graphics/Palette.java
index 7a4e7ee..1462245 100644
--- a/v7/palette/src/main/java/android/support/v7/graphics/Palette.java
+++ b/v7/palette/src/main/java/android/support/v7/graphics/Palette.java
@@ -19,6 +19,8 @@
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.AsyncTask;
+import android.support.annotation.ColorInt;
+import android.support.annotation.Nullable;
import android.support.v4.graphics.ColorUtils;
import android.support.v4.os.AsyncTaskCompat;
import android.util.TimingLogger;
@@ -152,6 +154,7 @@
/**
* Returns the most vibrant swatch in the palette. Might be null.
*/
+ @Nullable
public Swatch getVibrantSwatch() {
return mGenerator.getVibrantSwatch();
}
@@ -159,6 +162,7 @@
/**
* Returns a light and vibrant swatch from the palette. Might be null.
*/
+ @Nullable
public Swatch getLightVibrantSwatch() {
return mGenerator.getLightVibrantSwatch();
}
@@ -166,6 +170,7 @@
/**
* Returns a dark and vibrant swatch from the palette. Might be null.
*/
+ @Nullable
public Swatch getDarkVibrantSwatch() {
return mGenerator.getDarkVibrantSwatch();
}
@@ -173,6 +178,7 @@
/**
* Returns a muted swatch from the palette. Might be null.
*/
+ @Nullable
public Swatch getMutedSwatch() {
return mGenerator.getMutedSwatch();
}
@@ -180,6 +186,7 @@
/**
* Returns a muted and light swatch from the palette. Might be null.
*/
+ @Nullable
public Swatch getLightMutedSwatch() {
return mGenerator.getLightMutedSwatch();
}
@@ -187,6 +194,7 @@
/**
* Returns a muted and dark swatch from the palette. Might be null.
*/
+ @Nullable
public Swatch getDarkMutedSwatch() {
return mGenerator.getDarkMutedSwatch();
}
@@ -196,7 +204,8 @@
*
* @param defaultColor value to return if the swatch isn't available
*/
- public int getVibrantColor(int defaultColor) {
+ @ColorInt
+ public int getVibrantColor(@ColorInt int defaultColor) {
Swatch swatch = getVibrantSwatch();
return swatch != null ? swatch.getRgb() : defaultColor;
}
@@ -206,7 +215,8 @@
*
* @param defaultColor value to return if the swatch isn't available
*/
- public int getLightVibrantColor(int defaultColor) {
+ @ColorInt
+ public int getLightVibrantColor(@ColorInt int defaultColor) {
Swatch swatch = getLightVibrantSwatch();
return swatch != null ? swatch.getRgb() : defaultColor;
}
@@ -216,7 +226,8 @@
*
* @param defaultColor value to return if the swatch isn't available
*/
- public int getDarkVibrantColor(int defaultColor) {
+ @ColorInt
+ public int getDarkVibrantColor(@ColorInt int defaultColor) {
Swatch swatch = getDarkVibrantSwatch();
return swatch != null ? swatch.getRgb() : defaultColor;
}
@@ -226,7 +237,8 @@
*
* @param defaultColor value to return if the swatch isn't available
*/
- public int getMutedColor(int defaultColor) {
+ @ColorInt
+ public int getMutedColor(@ColorInt int defaultColor) {
Swatch swatch = getMutedSwatch();
return swatch != null ? swatch.getRgb() : defaultColor;
}
@@ -236,7 +248,8 @@
*
* @param defaultColor value to return if the swatch isn't available
*/
- public int getLightMutedColor(int defaultColor) {
+ @ColorInt
+ public int getLightMutedColor(@ColorInt int defaultColor) {
Swatch swatch = getLightMutedSwatch();
return swatch != null ? swatch.getRgb() : defaultColor;
}
@@ -246,7 +259,8 @@
*
* @param defaultColor value to return if the swatch isn't available
*/
- public int getDarkMutedColor(int defaultColor) {
+ @ColorInt
+ public int getDarkMutedColor(@ColorInt int defaultColor) {
Swatch swatch = getDarkMutedSwatch();
return swatch != null ? swatch.getRgb() : defaultColor;
}
@@ -285,7 +299,7 @@
private float[] mHsl;
- public Swatch(int color, int population) {
+ public Swatch(@ColorInt int color, int population) {
mRed = Color.red(color);
mGreen = Color.green(color);
mBlue = Color.blue(color);
@@ -304,6 +318,7 @@
/**
* @return this swatch's RGB color value
*/
+ @ColorInt
public int getRgb() {
return mRgb;
}
@@ -333,6 +348,7 @@
* Returns an appropriate color to use for any 'title' text which is displayed over this
* {@link Swatch}'s color. This color is guaranteed to have sufficient contrast.
*/
+ @ColorInt
public int getTitleTextColor() {
ensureTextColorsGenerated();
return mTitleTextColor;
@@ -342,6 +358,7 @@
* Returns an appropriate color to use for any 'body' text which is displayed over this
* {@link Swatch}'s color. This color is guaranteed to have sufficient contrast.
*/
+ @ColorInt
public int getBodyTextColor() {
ensureTextColorsGenerated();
return mBodyTextColor;
diff --git a/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java b/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java
index 8a773e8..18d385c 100644
--- a/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java
+++ b/v8/renderscript/java/src/android/support/v8/renderscript/RenderScript.java
@@ -822,8 +822,12 @@
long[] fieldIDs, long[] values, int[] sizes, long[] depClosures,
long[] depFieldIDs) {
validate();
- return rsnClosureCreate(mContext, kernelID, returnValue, fieldIDs, values,
+ long c = rsnClosureCreate(mContext, kernelID, returnValue, fieldIDs, values,
sizes, depClosures, depFieldIDs);
+ if (c == 0) {
+ throw new RSRuntimeException("Failed creating closure.");
+ }
+ return c;
}
native long rsnInvokeClosureCreate(long con, long invokeID, byte[] params,
@@ -831,8 +835,12 @@
synchronized long nInvokeClosureCreate(long invokeID, byte[] params,
long[] fieldIDs, long[] values, int[] sizes) {
validate();
- return rsnInvokeClosureCreate(mContext, invokeID, params, fieldIDs,
+ long c = rsnInvokeClosureCreate(mContext, invokeID, params, fieldIDs,
values, sizes);
+ if (c == 0) {
+ throw new RSRuntimeException("Failed creating closure.");
+ }
+ return c;
}
native void rsnClosureSetArg(long con, long closureID, int index,
diff --git a/v8/renderscript/jni/android_renderscript_RenderScript.cpp b/v8/renderscript/jni/android_renderscript_RenderScript.cpp
index d22c502..525c6bd 100644
--- a/v8/renderscript/jni/android_renderscript_RenderScript.cpp
+++ b/v8/renderscript/jni/android_renderscript_RenderScript.cpp
@@ -345,79 +345,167 @@
jlong returnValue, jlongArray fieldIDArray,
jlongArray valueArray, jintArray sizeArray,
jlongArray depClosureArray, jlongArray depFieldIDArray) {
+ jlong ret = 0;
+
jlong* jFieldIDs = _env->GetLongArrayElements(fieldIDArray, nullptr);
jsize fieldIDs_length = _env->GetArrayLength(fieldIDArray);
- RsScriptFieldID* fieldIDs =
- (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * fieldIDs_length);
- for (int i = 0; i< fieldIDs_length; i++) {
- fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
- }
-
jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
jsize values_length = _env->GetArrayLength(valueArray);
- uintptr_t* values = (uintptr_t*)alloca(sizeof(uintptr_t) * values_length);
- for (int i = 0; i < values_length; i++) {
- values[i] = (uintptr_t)jValues[i];
- }
-
- jint* sizes = _env->GetIntArrayElements(sizeArray, nullptr);
+ jint* jSizes = _env->GetIntArrayElements(sizeArray, nullptr);
jsize sizes_length = _env->GetArrayLength(sizeArray);
-
jlong* jDepClosures =
_env->GetLongArrayElements(depClosureArray, nullptr);
jsize depClosures_length = _env->GetArrayLength(depClosureArray);
- RsClosure* depClosures =
- (RsClosure*)alloca(sizeof(RsClosure) * depClosures_length);
- for (int i = 0; i < depClosures_length; i++) {
- depClosures[i] = (RsClosure)jDepClosures[i];
- }
-
jlong* jDepFieldIDs =
_env->GetLongArrayElements(depFieldIDArray, nullptr);
jsize depFieldIDs_length = _env->GetArrayLength(depFieldIDArray);
- RsScriptFieldID* depFieldIDs =
- (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * depFieldIDs_length);
- for (int i = 0; i < depClosures_length; i++) {
+
+ size_t numValues, numDependencies;
+ RsScriptFieldID* fieldIDs;
+ uintptr_t* values;
+ RsClosure* depClosures;
+ RsScriptFieldID* depFieldIDs;
+
+ if (fieldIDs_length != values_length || values_length != sizes_length) {
+ LOG_API("Unmatched field IDs, values, and sizes in closure creation.");
+ goto exit;
+ }
+
+ numValues = (size_t)fieldIDs_length;
+
+ if (depClosures_length != depFieldIDs_length) {
+ LOG_API("Unmatched closures and field IDs for dependencies in closure creation.");
+ goto exit;
+ }
+
+ numDependencies = (size_t)depClosures_length;
+
+ if (numDependencies > numValues) {
+ LOG_API("Unexpected number of dependencies in closure creation");
+ goto exit;
+ }
+
+ if (numValues > RS_CLOSURE_MAX_NUMBER_ARGS_AND_BINDINGS) {
+ LOG_API("Too many arguments or globals in closure creation");
+ goto exit;
+ }
+
+ fieldIDs = (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * numValues);
+ if (fieldIDs == nullptr) {
+ goto exit;
+ }
+
+ for (size_t i = 0; i < numValues; i++) {
+ fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
+ }
+
+ values = (uintptr_t*)alloca(sizeof(uintptr_t) * numValues);
+ if (values == nullptr) {
+ goto exit;
+ }
+
+ for (size_t i = 0; i < numValues; i++) {
+ values[i] = (uintptr_t)jValues[i];
+ }
+
+ depClosures = (RsClosure*)alloca(sizeof(RsClosure) * numDependencies);
+ if (depClosures == nullptr) {
+ goto exit;
+ }
+
+ for (size_t i = 0; i < numDependencies; i++) {
+ depClosures[i] = (RsClosure)jDepClosures[i];
+ }
+
+ depFieldIDs = (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * numDependencies);
+ if (depFieldIDs == nullptr) {
+ goto exit;
+ }
+
+ for (size_t i = 0; i < numDependencies; i++) {
depFieldIDs[i] = (RsClosure)jDepFieldIDs[i];
}
- return (jlong)(uintptr_t)dispatchTab.ClosureCreate(
+ ret = (jlong)(uintptr_t)dispatchTab.ClosureCreate(
(RsContext)con, (RsScriptKernelID)kernelID, (RsAllocation)returnValue,
- fieldIDs, (size_t)fieldIDs_length, values, (size_t)values_length,
- (int*)sizes, (size_t)sizes_length,
- depClosures, (size_t)depClosures_length,
- depFieldIDs, (size_t)depFieldIDs_length);
+ fieldIDs, numValues, values, numValues,
+ (int*)jSizes, numValues,
+ depClosures, numDependencies,
+ depFieldIDs, numDependencies);
+
+exit:
+
+ _env->ReleaseLongArrayElements(depFieldIDArray, jDepFieldIDs, JNI_ABORT);
+ _env->ReleaseLongArrayElements(depClosureArray, jDepClosures, JNI_ABORT);
+ _env->ReleaseIntArrayElements (sizeArray, jSizes, JNI_ABORT);
+ _env->ReleaseLongArrayElements(valueArray, jValues, JNI_ABORT);
+ _env->ReleaseLongArrayElements(fieldIDArray, jFieldIDs, JNI_ABORT);
+
+ return ret;
}
static jlong
nInvokeClosureCreate(JNIEnv *_env, jobject _this, jlong con, jlong invokeID,
jbyteArray paramArray, jlongArray fieldIDArray, jlongArray valueArray,
jintArray sizeArray) {
+ jlong ret = 0;
+
jbyte* jParams = _env->GetByteArrayElements(paramArray, nullptr);
jsize jParamLength = _env->GetArrayLength(paramArray);
-
jlong* jFieldIDs = _env->GetLongArrayElements(fieldIDArray, nullptr);
jsize fieldIDs_length = _env->GetArrayLength(fieldIDArray);
- RsScriptFieldID* fieldIDs =
- (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * fieldIDs_length);
- for (int i = 0; i< fieldIDs_length; i++) {
+ jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
+ jsize values_length = _env->GetArrayLength(valueArray);
+ jint* jSizes = _env->GetIntArrayElements(sizeArray, nullptr);
+ jsize sizes_length = _env->GetArrayLength(sizeArray);
+
+ size_t numValues;
+ RsScriptFieldID* fieldIDs;
+ uintptr_t* values;
+
+ if (fieldIDs_length != values_length || values_length != sizes_length) {
+ LOG_API("Unmatched field IDs, values, and sizes in closure creation.");
+ goto exit;
+ }
+
+ numValues = (size_t) fieldIDs_length;
+
+ if (numValues > RS_CLOSURE_MAX_NUMBER_ARGS_AND_BINDINGS) {
+ LOG_API("Too many arguments or globals in closure creation");
+ goto exit;
+ }
+
+ fieldIDs = (RsScriptFieldID*)alloca(sizeof(RsScriptFieldID) * numValues);
+ if (fieldIDs == nullptr) {
+ goto exit;
+ }
+
+ for (size_t i = 0; i < numValues; i++) {
fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
}
- jlong* jValues = _env->GetLongArrayElements(valueArray, nullptr);
- jsize values_length = _env->GetArrayLength(valueArray);
- uintptr_t* values = (uintptr_t*)alloca(sizeof(uintptr_t) * values_length);
- for (int i = 0; i < values_length; i++) {
+ values = (uintptr_t*)alloca(sizeof(uintptr_t) * numValues);
+ if (values == nullptr) {
+ goto exit;
+ }
+
+ for (size_t i = 0; i < numValues; i++) {
values[i] = (uintptr_t)jValues[i];
}
- jint* sizes = _env->GetIntArrayElements(sizeArray, nullptr);
- jsize sizes_length = _env->GetArrayLength(sizeArray);
-
- return (jlong)(uintptr_t)dispatchTab.InvokeClosureCreate(
+ ret = (jlong)(uintptr_t)dispatchTab.InvokeClosureCreate(
(RsContext)con, (RsScriptInvokeID)invokeID, jParams, jParamLength,
- fieldIDs, (size_t)fieldIDs_length, values, (size_t)values_length,
- (int*)sizes, (size_t)sizes_length);
+ fieldIDs, numValues, values, numValues,
+ (int*)jSizes, numValues);
+
+exit:
+
+ _env->ReleaseIntArrayElements (sizeArray, jSizes, JNI_ABORT);
+ _env->ReleaseLongArrayElements(valueArray, jValues, JNI_ABORT);
+ _env->ReleaseLongArrayElements(fieldIDArray, jFieldIDs, JNI_ABORT);
+ _env->ReleaseByteArrayElements(paramArray, jParams, JNI_ABORT);
+
+ return ret;
}
static void
@@ -437,20 +525,40 @@
static long
nScriptGroup2Create(JNIEnv *_env, jobject _this, jlong con, jstring name,
jstring cacheDir, jlongArray closureArray) {
+ jlong ret = 0;
+
AutoJavaStringToUTF8 nameUTF(_env, name);
AutoJavaStringToUTF8 cacheDirUTF(_env, cacheDir);
jlong* jClosures = _env->GetLongArrayElements(closureArray, nullptr);
jsize numClosures = _env->GetArrayLength(closureArray);
- RsClosure* closures = (RsClosure*)alloca(sizeof(RsClosure) * numClosures);
+
+ RsClosure* closures;
+
+ if (numClosures > (jsize) RS_SCRIPT_GROUP_MAX_NUMBER_CLOSURES) {
+ LOG_API("Too many closures in script group");
+ goto exit;
+ }
+
+ closures = (RsClosure*)alloca(sizeof(RsClosure) * numClosures);
+ if (closures == nullptr) {
+ goto exit;
+ }
+
for (int i = 0; i < numClosures; i++) {
closures[i] = (RsClosure)jClosures[i];
}
- return (jlong)(uintptr_t)dispatchTab.ScriptGroup2Create(
+ ret = (jlong)(uintptr_t)dispatchTab.ScriptGroup2Create(
(RsContext)con, nameUTF.c_str(), nameUTF.length(),
cacheDirUTF.c_str(), cacheDirUTF.length(),
closures, numClosures);
+
+exit:
+
+ _env->ReleaseLongArrayElements(closureArray, jClosures, JNI_ABORT);
+
+ return ret;
}
static void
@@ -1138,7 +1246,7 @@
(void *)script, slot, val);
if (mUseInc) {
dispatchTabInc.ScriptSetVarD((RsContext)con, (RsScript)script, slot, val);
- } else {
+ } else {
dispatchTab.ScriptSetVarD((RsContext)con, (RsScript)script, slot, val);
}
}
@@ -1389,7 +1497,7 @@
(void *)sid, slot, sig);
if (mUseInc) {
return (jlong)(uintptr_t)dispatchTabInc.ScriptKernelIDCreate((RsContext)con, (RsScript)sid,
- slot, sig);
+ slot, sig);
} else {
return (jlong)(uintptr_t)dispatchTab.ScriptKernelIDCreate((RsContext)con, (RsScript)sid,
slot, sig);