Merge "Ensure NotificationCompat.MediaStyle methods return MediaStyle" into lmp-mr1-ub-dev
diff --git a/design/api/current.txt b/design/api/current.txt
index 217ba99..9dc6e20 100644
--- a/design/api/current.txt
+++ b/design/api/current.txt
@@ -170,7 +170,9 @@
ctor public FloatingActionButton(android.content.Context);
ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet);
ctor public FloatingActionButton(android.content.Context, android.util.AttributeSet, int);
+ method public void hide();
method public void setRippleColor(int);
+ method public void show();
}
public static class FloatingActionButton.Behavior extends android.support.design.widget.CoordinatorLayout.Behavior {
diff --git a/design/base/android/support/design/widget/FloatingActionButtonImpl.java b/design/base/android/support/design/widget/FloatingActionButtonImpl.java
index 6c154d4..7fcb147 100644
--- a/design/base/android/support/design/widget/FloatingActionButtonImpl.java
+++ b/design/base/android/support/design/widget/FloatingActionButtonImpl.java
@@ -26,6 +26,8 @@
abstract class FloatingActionButtonImpl {
+ static final int SHOW_HIDE_ANIM_DURATION = 200;
+
static final int[] PRESSED_ENABLED_STATE_SET = {android.R.attr.state_pressed,
android.R.attr.state_enabled};
static final int[] FOCUSED_ENABLED_STATE_SET = {android.R.attr.state_focused,
@@ -57,6 +59,10 @@
abstract void jumpDrawableToCurrentState();
+ abstract void hide();
+
+ abstract void show();
+
Drawable createBorderDrawable(int borderWidth, ColorStateList backgroundTint) {
final Resources resources = mView.getResources();
CircularBorderDrawable borderDrawable = newCircularDrawable();
diff --git a/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java b/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java
index 36ddd89..1aa0b683 100644
--- a/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java
+++ b/design/eclair-mr1/android/support/design/widget/FloatingActionButtonEclairMr1.java
@@ -23,6 +23,7 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
+import android.support.design.R;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.view.View;
import android.view.animation.Animation;
@@ -42,6 +43,8 @@
ShadowDrawableWrapper mShadowDrawable;
+ private boolean mIsHiding;
+
FloatingActionButtonEclairMr1(View view, ShadowViewDelegate shadowViewDelegate) {
super(view, shadowViewDelegate);
@@ -153,6 +156,41 @@
mStateListAnimator.jumpToCurrentState();
}
+ @Override
+ void hide() {
+ if (mIsHiding) {
+ // There is currently an hide animation running, return
+ return;
+ }
+
+ Animation anim = android.view.animation.AnimationUtils.loadAnimation(
+ mView.getContext(), R.anim.fab_out);
+ anim.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
+ anim.setDuration(SHOW_HIDE_ANIM_DURATION);
+ anim.setAnimationListener(new AnimationUtils.AnimationListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ mIsHiding = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ mIsHiding = false;
+ mView.setVisibility(View.GONE);
+ }
+ });
+ mView.startAnimation(anim);
+ }
+
+ @Override
+ void show() {
+ Animation anim = android.view.animation.AnimationUtils.loadAnimation(
+ mView.getContext(), R.anim.fab_in);
+ anim.setDuration(SHOW_HIDE_ANIM_DURATION);
+ anim.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
+ mView.startAnimation(anim);
+ }
+
private void updatePadding() {
Rect rect = new Rect();
mShadowDrawable.getPadding(rect);
diff --git a/design/honeycomb-mr1/android/support/design/widget/FloatingActionButtonHoneycombMr1.java b/design/honeycomb-mr1/android/support/design/widget/FloatingActionButtonHoneycombMr1.java
new file mode 100644
index 0000000..62da58e
--- /dev/null
+++ b/design/honeycomb-mr1/android/support/design/widget/FloatingActionButtonHoneycombMr1.java
@@ -0,0 +1,73 @@
+/*
+ * 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.design.widget;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.view.View;
+
+class FloatingActionButtonHoneycombMr1 extends FloatingActionButtonEclairMr1 {
+
+ private boolean mIsHiding;
+
+ FloatingActionButtonHoneycombMr1(View view, ShadowViewDelegate shadowViewDelegate) {
+ super(view, shadowViewDelegate);
+ }
+
+ @Override
+ void hide() {
+ if (mIsHiding) {
+ // A hide animation is in progress, skip the call
+ return;
+ }
+
+ mView.animate()
+ .scaleX(0f)
+ .scaleY(0f)
+ .alpha(0f)
+ .setDuration(SHOW_HIDE_ANIM_DURATION)
+ .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mIsHiding = true;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mIsHiding = false;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mIsHiding = false;
+ mView.setVisibility(View.GONE);
+ }
+ });
+ }
+
+ @Override
+ void show() {
+ mView.animate()
+ .scaleX(1f)
+ .scaleY(1f)
+ .alpha(1f)
+ .setDuration(SHOW_HIDE_ANIM_DURATION)
+ .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
+ .setListener(null);
+ }
+}
diff --git a/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
index c21d341..a440427 100644
--- a/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
+++ b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
@@ -33,7 +33,7 @@
import android.view.animation.Interpolator;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
-class FloatingActionButtonLollipop extends FloatingActionButtonImpl {
+class FloatingActionButtonLollipop extends FloatingActionButtonHoneycombMr1 {
private Drawable mShapeDrawable;
private RippleDrawable mRippleDrawable;
diff --git a/design/res/layout-sw600dp/layout_snackbar.xml b/design/res/layout-sw600dp/layout_snackbar.xml
index 6605408..b68395a 100644
--- a/design/res/layout-sw600dp/layout_snackbar.xml
+++ b/design/res/layout-sw600dp/layout_snackbar.xml
@@ -19,5 +19,5 @@
class="android.support.design.widget.Snackbar$SnackbarLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="bottom|center_vertical"
+ android:layout_gravity="bottom|center_horizontal"
style="@style/Widget.Design.Snackbar" />
\ No newline at end of file
diff --git a/design/res/layout/layout_tab_icon.xml b/design/res/layout/layout_tab_icon.xml
new file mode 100644
index 0000000..6464d1f
--- /dev/null
+++ b/design/res/layout/layout_tab_icon.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"/>
\ No newline at end of file
diff --git a/design/res/layout/layout_tab_text.xml b/design/res/layout/layout_tab_text.xml
new file mode 100644
index 0000000..a83bb3d
--- /dev/null
+++ b/design/res/layout/layout_tab_text.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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:gravity="center"
+ android:maxLines="2"/>
\ No newline at end of file
diff --git a/design/src/android/support/design/internal/NavigationMenuPresenter.java b/design/src/android/support/design/internal/NavigationMenuPresenter.java
index f19b78a..6f374d3 100644
--- a/design/src/android/support/design/internal/NavigationMenuPresenter.java
+++ b/design/src/android/support/design/internal/NavigationMenuPresenter.java
@@ -463,7 +463,9 @@
public Bundle createInstanceState() {
Bundle state = new Bundle();
- state.putInt(STATE_CHECKED_ITEM, mCheckedItem.getItemId());
+ if (mCheckedItem != null) {
+ state.putInt(STATE_CHECKED_ITEM, mCheckedItem.getItemId());
+ }
return state;
}
diff --git a/design/src/android/support/design/widget/FloatingActionButton.java b/design/src/android/support/design/widget/FloatingActionButton.java
index bd2ce10..13262a2 100644
--- a/design/src/android/support/design/widget/FloatingActionButton.java
+++ b/design/src/android/support/design/widget/FloatingActionButton.java
@@ -27,10 +27,8 @@
import android.support.annotation.Nullable;
import android.support.design.R;
import android.support.v4.view.ViewCompat;
-import android.support.v4.view.ViewPropertyAnimatorListener;
import android.util.AttributeSet;
import android.view.View;
-import android.view.animation.Animation;
import android.widget.ImageView;
import java.util.List;
@@ -119,8 +117,11 @@
}
};
- if (Build.VERSION.SDK_INT >= 21) {
+ final int sdk = Build.VERSION.SDK_INT;
+ if (sdk >= 21) {
mImpl = new FloatingActionButtonLollipop(this, delegate);
+ } else if (sdk >= 12) {
+ mImpl = new FloatingActionButtonHoneycombMr1(this, delegate);
} else {
mImpl = new FloatingActionButtonEclairMr1(this, delegate);
}
@@ -220,7 +221,36 @@
public void setBackgroundDrawable(Drawable background) {
if (mImpl != null) {
mImpl.setBackgroundDrawable(
- background, mBackgroundTint, mBackgroundTintMode, mRippleColor, mBorderWidth);
+ background, mBackgroundTint, mBackgroundTintMode, mRippleColor, mBorderWidth);
+ }
+ }
+
+ /**
+ * Shows the button.
+ * <p>This method will animate it the button show if the view has already been laid out.</p>
+ */
+ public void show() {
+ if (getVisibility() == VISIBLE) {
+ return;
+ }
+ setVisibility(VISIBLE);
+ if (ViewCompat.isLaidOut(this)) {
+ mImpl.show();
+ }
+ }
+
+ /**
+ * Hides the button.
+ * <p>This method will animate the button hide if the view has already been laid out.</p>
+ */
+ public void hide() {
+ if (getVisibility() != VISIBLE) {
+ return;
+ }
+ if (ViewCompat.isLaidOut(this)) {
+ mImpl.hide();
+ } else {
+ setVisibility(GONE);
}
}
@@ -299,13 +329,11 @@
private static final boolean SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= 11;
private Rect mTmpRect;
- private boolean mIsAnimatingOut;
private float mTranslationY;
@Override
public boolean layoutDependsOn(CoordinatorLayout parent,
- FloatingActionButton child,
- View dependency) {
+ FloatingActionButton child, View dependency) {
// We're dependent on all SnackbarLayouts (if enabled)
return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout;
}
@@ -316,32 +344,47 @@
if (dependency instanceof Snackbar.SnackbarLayout) {
updateFabTranslationForSnackbar(parent, child, dependency);
} else if (dependency instanceof AppBarLayout) {
- final AppBarLayout appBarLayout = (AppBarLayout) dependency;
- if (mTmpRect == null) {
- mTmpRect = new Rect();
- }
-
- // First, let's get the visible rect of the dependency
- final Rect rect = mTmpRect;
- ViewGroupUtils.getDescendantRect(parent, dependency, rect);
-
- if (rect.bottom <= appBarLayout.getMinimumHeightForVisibleOverlappingContent()) {
- // If the anchor's bottom is below the seam, we'll animate our FAB out
- if (!mIsAnimatingOut && child.getVisibility() == View.VISIBLE) {
- animateOut(child);
- }
- } else {
- // Else, we'll animate our FAB back in
- if (child.getVisibility() != View.VISIBLE) {
- animateIn(child);
- }
- }
+ // If we're depending on an AppBarLayout we will show/hide it automatically
+ // if the FAB is anchored to the AppBarLayout
+ updateFabVisibility(parent, (AppBarLayout) dependency, child);
}
return false;
}
+ private boolean updateFabVisibility(CoordinatorLayout parent,
+ AppBarLayout appBarLayout, FloatingActionButton child) {
+ final CoordinatorLayout.LayoutParams lp =
+ (CoordinatorLayout.LayoutParams) child.getLayoutParams();
+ if (lp.getAnchorId() != appBarLayout.getId()) {
+ // The anchor ID doesn't match the dependency, so we won't automatically
+ // show/hide the FAB
+ return false;
+ }
+
+ if (mTmpRect == null) {
+ mTmpRect = new Rect();
+ }
+
+ // First, let's get the visible rect of the dependency
+ final Rect rect = mTmpRect;
+ ViewGroupUtils.getDescendantRect(parent, appBarLayout, rect);
+
+ if (rect.bottom <= appBarLayout.getMinimumHeightForVisibleOverlappingContent()) {
+ // If the anchor's bottom is below the seam, we'll animate our FAB out
+ child.hide();
+ } else {
+ // Else, we'll animate our FAB back in
+ child.show();
+ }
+ return true;
+ }
+
private void updateFabTranslationForSnackbar(CoordinatorLayout parent,
FloatingActionButton fab, View snackbar) {
+ if (fab.getVisibility() != View.VISIBLE) {
+ return;
+ }
+
final float translationY = getFabTranslationYForSnackbar(parent, fab);
if (translationY != mTranslationY) {
// First, cancel any current animation
@@ -377,83 +420,25 @@
return minOffset;
}
- private void animateIn(FloatingActionButton button) {
- button.setVisibility(View.VISIBLE);
-
- if (Build.VERSION.SDK_INT >= 14) {
- ViewCompat.animate(button)
- .scaleX(1f)
- .scaleY(1f)
- .alpha(1f)
- .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
- .withLayer()
- .setListener(null)
- .start();
- } else {
- Animation anim = android.view.animation.AnimationUtils.loadAnimation(
- button.getContext(), R.anim.fab_in);
- anim.setDuration(200);
- anim.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
- button.startAnimation(anim);
- }
- }
-
@Override
public boolean onLayoutChild(CoordinatorLayout parent, FloatingActionButton child,
int layoutDirection) {
- // Let the CoordinatorLayout lay out the FAB
+ // First, lets make sure that the visibility of the FAB is consistent
+ final List<View> dependencies = parent.getDependencies(child);
+ for (int i = 0, count = dependencies.size(); i < count; i++) {
+ final View dependency = dependencies.get(i);
+ if (dependency instanceof AppBarLayout
+ && updateFabVisibility(parent, (AppBarLayout) dependency, child)) {
+ break;
+ }
+ }
+ // Now let the CoordinatorLayout lay out the FAB
parent.onLayoutChild(child, layoutDirection);
// Now offset it if needed
offsetIfNeeded(parent, child);
return true;
}
- private void animateOut(final FloatingActionButton button) {
- if (Build.VERSION.SDK_INT >= 14) {
- ViewCompat.animate(button)
- .scaleX(0f)
- .scaleY(0f)
- .alpha(0f)
- .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR)
- .withLayer()
- .setListener(new ViewPropertyAnimatorListener() {
- @Override
- public void onAnimationStart(View view) {
- mIsAnimatingOut = true;
- }
-
- @Override
- public void onAnimationCancel(View view) {
- mIsAnimatingOut = false;
- }
-
- @Override
- public void onAnimationEnd(View view) {
- mIsAnimatingOut = false;
- view.setVisibility(View.GONE);
- }
- }).start();
- } else {
- Animation anim = android.view.animation.AnimationUtils.loadAnimation(
- button.getContext(), R.anim.fab_out);
- anim.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
- anim.setDuration(200);
- anim.setAnimationListener(new AnimationUtils.AnimationListenerAdapter() {
- @Override
- public void onAnimationStart(Animation animation) {
- mIsAnimatingOut = true;
- }
-
- @Override
- public void onAnimationEnd(Animation animation) {
- mIsAnimatingOut = false;
- button.setVisibility(View.GONE);
- }
- });
- button.startAnimation(anim);
- }
- }
-
/**
* Pre-Lollipop we use padding so that the shadow has enough space to be drawn. This method
* offsets our layout position so that we're positioned correctly if we're on one of
diff --git a/design/src/android/support/design/widget/TabLayout.java b/design/src/android/support/design/widget/TabLayout.java
index f2e44ec..6e0153a 100755
--- a/design/src/android/support/design/widget/TabLayout.java
+++ b/design/src/android/support/design/widget/TabLayout.java
@@ -25,7 +25,12 @@
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.ColorInt;
+import android.support.annotation.DrawableRes;
import android.support.annotation.IntDef;
+import android.support.annotation.LayoutRes;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.annotation.StringRes;
import android.support.design.R;
import android.support.v4.view.GravityCompat;
import android.support.v4.view.PagerAdapter;
@@ -33,7 +38,6 @@
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.internal.widget.TintManager;
-import android.support.v7.widget.AppCompatTextView;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.Gravity;
@@ -330,7 +334,7 @@
*
* @param tab Tab to add
*/
- public void addTab(Tab tab) {
+ public void addTab(@NonNull Tab tab) {
addTab(tab, mTabs.isEmpty());
}
@@ -341,7 +345,7 @@
* @param tab The tab to add
* @param position The new position of the tab
*/
- public void addTab(Tab tab, int position) {
+ public void addTab(@NonNull Tab tab, int position) {
addTab(tab, position, mTabs.isEmpty());
}
@@ -351,7 +355,7 @@
* @param tab Tab to add
* @param setSelected True if the added tab should become the selected tab.
*/
- public void addTab(Tab tab, boolean setSelected) {
+ public void addTab(@NonNull Tab tab, boolean setSelected) {
if (tab.mParent != this) {
throw new IllegalArgumentException("Tab belongs to a different TabLayout.");
}
@@ -370,7 +374,7 @@
* @param position The new position of the tab
* @param setSelected True if the added tab should become the selected tab.
*/
- public void addTab(Tab tab, int position, boolean setSelected) {
+ public void addTab(@NonNull Tab tab, int position, boolean setSelected) {
if (tab.mParent != this) {
throw new IllegalArgumentException("Tab belongs to a different TabLayout.");
}
@@ -399,6 +403,7 @@
* @return A new Tab
* @see #addTab(Tab)
*/
+ @NonNull
public Tab newTab() {
return new Tab(this);
}
@@ -415,6 +420,7 @@
/**
* Returns the tab at the specified index.
*/
+ @Nullable
public Tab getTabAt(int index) {
return mTabs.get(index);
}
@@ -529,7 +535,7 @@
/**
* Sets the text colors for the different states (normal, selected) used for the tabs.
*/
- public void setTabTextColors(ColorStateList textColor) {
+ public void setTabTextColors(@Nullable ColorStateList textColor) {
if (mTabTextColors != textColor) {
mTabTextColors = textColor;
updateAllTabs();
@@ -539,6 +545,7 @@
/**
* Gets the text colors for the different states (normal, selected) used for the tabs.
*/
+ @Nullable
public ColorStateList getTabTextColors() {
return mTabTextColors;
}
@@ -567,7 +574,7 @@
* @see TabLayoutOnPageChangeListener
* @see ViewPagerOnTabSelectedListener
*/
- public void setupWithViewPager(ViewPager viewPager) {
+ public void setupWithViewPager(@NonNull ViewPager viewPager) {
final PagerAdapter adapter = viewPager.getAdapter();
if (adapter == null) {
throw new IllegalArgumentException("ViewPager does not have a PagerAdapter set");
@@ -597,7 +604,7 @@
*
* @param adapter the adapter to populate from
*/
- public void setTabsFromPagerAdapter(PagerAdapter adapter) {
+ public void setTabsFromPagerAdapter(@NonNull PagerAdapter adapter) {
removeAllTabs();
for (int i = 0, count = adapter.getCount(); i < count; i++) {
addTab(newTab().setText(adapter.getPageTitle(i)));
@@ -880,6 +887,7 @@
/**
* @return This Tab's tag object.
*/
+ @Nullable
public Object getTag() {
return mTag;
}
@@ -890,7 +898,8 @@
* @param tag Object to store
* @return The current instance for call chaining
*/
- public Tab setTag(Object tag) {
+ @NonNull
+ public Tab setTag(@Nullable Object tag) {
mTag = tag;
return this;
}
@@ -900,13 +909,20 @@
}
/**
- * Set a custom view to be used for this tab. This overrides values set by {@link
- * #setText(CharSequence)} and {@link #setIcon(Drawable)}.
+ * Set a custom view to be used for this tab.
+ * <p>
+ * If the provided view contains a {@link TextView} with an ID of
+ * {@link android.R.id#text1} then that will be updated with the value given
+ * to {@link #setText(CharSequence)}. Similarly, if this layout contains an
+ * {@link ImageView} with ID {@link android.R.id#icon} then it will be updated with
+ * the value given to {@link #setIcon(Drawable)}.
+ * </p>
*
* @param view Custom view to be used as a tab.
* @return The current instance for call chaining
*/
- public Tab setCustomView(View view) {
+ @NonNull
+ public Tab setCustomView(@Nullable View view) {
mCustomView = view;
if (mPosition >= 0) {
mParent.updateTab(mPosition);
@@ -915,13 +931,20 @@
}
/**
- * Set a custom view to be used for this tab. This overrides values set by {@link
- * #setText(CharSequence)} and {@link #setIcon(Drawable)}.
+ * Set a custom view to be used for this tab.
+ * <p>
+ * If the inflated layout contains a {@link TextView} with an ID of
+ * {@link android.R.id#text1} then that will be updated with the value given
+ * to {@link #setText(CharSequence)}. Similarly, if this layout contains an
+ * {@link ImageView} with ID {@link android.R.id#icon} then it will be updated with
+ * the value given to {@link #setIcon(Drawable)}.
+ * </p>
*
* @param layoutResId A layout resource to inflate and use as a custom tab view
* @return The current instance for call chaining
*/
- public Tab setCustomView(int layoutResId) {
+ @NonNull
+ public Tab setCustomView(@LayoutRes int layoutResId) {
return setCustomView(
LayoutInflater.from(mParent.getContext()).inflate(layoutResId, null));
}
@@ -931,6 +954,7 @@
*
* @return The tab's icon
*/
+ @Nullable
public Drawable getIcon() {
return mIcon;
}
@@ -954,6 +978,7 @@
*
* @return The tab's text
*/
+ @Nullable
public CharSequence getText() {
return mText;
}
@@ -964,7 +989,8 @@
* @param icon The drawable to use as an icon
* @return The current instance for call chaining
*/
- public Tab setIcon(Drawable icon) {
+ @NonNull
+ public Tab setIcon(@Nullable Drawable icon) {
mIcon = icon;
if (mPosition >= 0) {
mParent.updateTab(mPosition);
@@ -978,7 +1004,8 @@
* @param resId A resource ID referring to the icon that should be displayed
* @return The current instance for call chaining
*/
- public Tab setIcon(int resId) {
+ @NonNull
+ public Tab setIcon(@DrawableRes int resId) {
return setIcon(TintManager.getDrawable(mParent.getContext(), resId));
}
@@ -989,7 +1016,8 @@
* @param text The text to display
* @return The current instance for call chaining
*/
- public Tab setText(CharSequence text) {
+ @NonNull
+ public Tab setText(@Nullable CharSequence text) {
mText = text;
if (mPosition >= 0) {
mParent.updateTab(mPosition);
@@ -1004,7 +1032,8 @@
* @param resId A resource ID referring to the text that should be displayed
* @return The current instance for call chaining
*/
- public Tab setText(int resId) {
+ @NonNull
+ public Tab setText(@StringRes int resId) {
return setText(mParent.getResources().getText(resId));
}
@@ -1024,7 +1053,8 @@
* @see #setContentDescription(CharSequence)
* @see #getContentDescription()
*/
- public Tab setContentDescription(int resId) {
+ @NonNull
+ public Tab setContentDescription(@StringRes int resId) {
return setContentDescription(mParent.getResources().getText(resId));
}
@@ -1037,7 +1067,8 @@
* @see #setContentDescription(int)
* @see #getContentDescription()
*/
- public Tab setContentDescription(CharSequence contentDesc) {
+ @NonNull
+ public Tab setContentDescription(@Nullable CharSequence contentDesc) {
mContentDesc = contentDesc;
if (mPosition >= 0) {
mParent.updateTab(mPosition);
@@ -1052,6 +1083,7 @@
* @see #setContentDescription(CharSequence)
* @see #setContentDescription(int)
*/
+ @Nullable
public CharSequence getContentDescription() {
return mContentDesc;
}
@@ -1061,7 +1093,10 @@
private final Tab mTab;
private TextView mTextView;
private ImageView mIconView;
+
private View mCustomView;
+ private TextView mCustomTextView;
+ private ImageView mCustomIconView;
public TabView(Context context, Tab tab) {
super(context);
@@ -1141,64 +1176,80 @@
mIconView.setVisibility(GONE);
mIconView.setImageDrawable(null);
}
+
+ mCustomTextView = (TextView) custom.findViewById(android.R.id.text1);
+ mCustomIconView = (ImageView) custom.findViewById(android.R.id.icon);
} else {
+ // We do not have a custom view. Remove one if it already exists
if (mCustomView != null) {
removeView(mCustomView);
mCustomView = null;
}
+ mCustomTextView = null;
+ mCustomIconView = null;
+ }
- final Drawable icon = tab.getIcon();
- final CharSequence text = tab.getText();
+ if (mCustomView == null) {
+ // If there isn't a custom view, we'll us our own in-built layouts
+ if (mIconView == null) {
+ ImageView iconView = (ImageView) LayoutInflater.from(getContext())
+ .inflate(R.layout.layout_tab_icon, this, false);
+ addView(iconView, 0);
+ mIconView = iconView;
+ }
+ if (mTextView == null) {
+ TextView textView = (TextView) LayoutInflater.from(getContext())
+ .inflate(R.layout.layout_tab_text, this, false);
+ addView(textView);
+ mTextView = textView;
+ }
+ mTextView.setTextAppearance(getContext(), mTabTextAppearance);
+ if (mTabTextColors != null) {
+ mTextView.setTextColor(mTabTextColors);
+ }
+ updateTextAndIcon(tab, mTextView, mIconView);
+ } else {
+ // Else, we'll see if there is a TextView or ImageView present and update them
+ if (mCustomTextView != null || mCustomIconView != null) {
+ updateTextAndIcon(tab, mCustomTextView, mCustomIconView);
+ }
+ }
+ }
+ private void updateTextAndIcon(Tab tab, TextView textView, ImageView iconView) {
+ final Drawable icon = tab.getIcon();
+ final CharSequence text = tab.getText();
+
+ if (iconView != null) {
if (icon != null) {
- if (mIconView == null) {
- ImageView iconView = new ImageView(getContext());
- LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
- LayoutParams.WRAP_CONTENT);
- lp.gravity = Gravity.CENTER_VERTICAL;
- iconView.setLayoutParams(lp);
- addView(iconView, 0);
- mIconView = iconView;
- }
- mIconView.setImageDrawable(icon);
- mIconView.setVisibility(VISIBLE);
- } else if (mIconView != null) {
- mIconView.setVisibility(GONE);
- mIconView.setImageDrawable(null);
- }
-
- final boolean hasText = !TextUtils.isEmpty(text);
- if (hasText) {
- if (mTextView == null) {
- AppCompatTextView textView = new AppCompatTextView(getContext());
- textView.setMaxLines(MAX_TAB_TEXT_LINES);
- textView.setEllipsize(TextUtils.TruncateAt.END);
- textView.setGravity(Gravity.CENTER);
- addView(textView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
- mTextView = textView;
- }
- mTextView.setTextAppearance(getContext(), mTabTextAppearance);
- if (mTabTextColors != null) {
- mTextView.setTextColor(mTabTextColors);
- }
- mTextView.setText(text);
- mTextView.setContentDescription(tab.getContentDescription());
- mTextView.setVisibility(VISIBLE);
- } else if (mTextView != null) {
- mTextView.setVisibility(GONE);
- mTextView.setText(null);
- }
-
- if (mIconView != null) {
- mIconView.setContentDescription(tab.getContentDescription());
- }
-
- if (!hasText && !TextUtils.isEmpty(tab.getContentDescription())) {
- setOnLongClickListener(this);
+ iconView.setImageDrawable(icon);
+ iconView.setVisibility(VISIBLE);
+ setVisibility(VISIBLE);
} else {
- setOnLongClickListener(null);
- setLongClickable(false);
+ iconView.setVisibility(GONE);
+ iconView.setImageDrawable(null);
}
+ iconView.setContentDescription(tab.getContentDescription());
+ }
+
+ final boolean hasText = !TextUtils.isEmpty(text);
+ if (textView != null) {
+ if (hasText) {
+ textView.setText(text);
+ textView.setContentDescription(tab.getContentDescription());
+ textView.setVisibility(VISIBLE);
+ setVisibility(VISIBLE);
+ } else {
+ textView.setVisibility(GONE);
+ textView.setText(null);
+ }
+ }
+
+ if (!hasText && !TextUtils.isEmpty(tab.getContentDescription())) {
+ setOnLongClickListener(this);
+ } else {
+ setOnLongClickListener(null);
+ setLongClickable(false);
}
}
diff --git a/v7/appcompat/api/current.txt b/v7/appcompat/api/current.txt
index cf6bc5c..fd3f7c3 100644
--- a/v7/appcompat/api/current.txt
+++ b/v7/appcompat/api/current.txt
@@ -250,6 +250,7 @@
method public abstract android.support.v7.app.ActionBarDrawerToggle.Delegate getDrawerToggleDelegate();
method public abstract android.view.MenuInflater getMenuInflater();
method public abstract android.support.v7.app.ActionBar getSupportActionBar();
+ method public abstract boolean hasWindowFeature(int);
method public abstract void installViewFactory();
method public abstract void invalidateOptionsMenu();
method public abstract boolean isHandleNativeActionModesEnabled();
diff --git a/v7/appcompat/res/values/themes_base.xml b/v7/appcompat/res/values/themes_base.xml
index 3e2c841..5db9d05 100644
--- a/v7/appcompat/res/values/themes_base.xml
+++ b/v7/appcompat/res/values/themes_base.xml
@@ -571,7 +571,7 @@
<item name="isLightTheme">true</item>
</style>
- <style name="Base.ThemeOverlay.AppCompat.Dark">
+ <style name="Base.ThemeOverlay.AppCompat.Dark" parent="Platform.ThemeOverlay.AppCompat.Dark">
<item name="android:windowBackground">@color/background_material_dark</item>
<item name="android:colorForeground">@color/bright_foreground_material_dark</item>
<item name="android:colorForegroundInverse">@color/bright_foreground_material_light</item>
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
index 8dbc827..e4283e3 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
@@ -55,8 +55,8 @@
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
getDelegate().installViewFactory();
- super.onCreate(savedInstanceState);
getDelegate().onCreate(savedInstanceState);
+ super.onCreate(savedInstanceState);
}
@Override
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java
index 8317ad2..3abe072 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java
@@ -214,6 +214,16 @@
public abstract boolean requestWindowFeature(int featureId);
/**
+ * Query for the availability of a certain feature.
+ *
+ * <p>This should be called instead of {@link android.view.Window#hasFeature(int)}.</p>
+ *
+ * @param featureId The feature ID to check
+ * @return true if the feature is enabled, false otherwise.
+ */
+ public abstract boolean hasWindowFeature(int featureId);
+
+ /**
* Start an action mode.
*
* @param callback Callback that will manage lifecycle events for this context mode
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV7.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV7.java
index abb665f..c777f57 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV7.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegateImplV7.java
@@ -503,6 +503,25 @@
}
@Override
+ public boolean hasWindowFeature(int featureId) {
+ switch (featureId) {
+ case FEATURE_ACTION_BAR:
+ return mHasActionBar;
+ case FEATURE_ACTION_BAR_OVERLAY:
+ return mOverlayActionBar;
+ case FEATURE_ACTION_MODE_OVERLAY:
+ return mOverlayActionMode;
+ case Window.FEATURE_PROGRESS:
+ return mFeatureProgress;
+ case Window.FEATURE_INDETERMINATE_PROGRESS:
+ return mFeatureIndeterminateProgress;
+ case Window.FEATURE_NO_TITLE:
+ return mWindowNoTitle;
+ }
+ return mWindow.hasFeature(featureId);
+ }
+
+ @Override
void onTitleChanged(CharSequence title) {
if (mDecorContentParent != null) {
mDecorContentParent.setWindowTitle(title);
@@ -1105,8 +1124,10 @@
mDecorContentParent.setMenuPrepared();
}
- if (st.createdPanelView == null) {
- // Init the panel state's menu--return false if init failed
+ if (st.createdPanelView == null &&
+ (!isActionBarMenu || !(peekSupportActionBar() instanceof ToolbarActionBar))) {
+ // Since ToolbarActionBar handles the list options menu itself, we only want to
+ // init this menu panel if we're not using a TAB.
if (st.menu == null || st.refreshMenuContent) {
if (st.menu == null) {
if (!initializePanelMenu(st) || (st.menu == null)) {
diff --git a/v7/appcompat/src/android/support/v7/internal/view/menu/ActionMenuItemView.java b/v7/appcompat/src/android/support/v7/internal/view/menu/ActionMenuItemView.java
index 05c92c9..a6c6dde 100644
--- a/v7/appcompat/src/android/support/v7/internal/view/menu/ActionMenuItemView.java
+++ b/v7/appcompat/src/android/support/v7/internal/view/menu/ActionMenuItemView.java
@@ -250,7 +250,8 @@
Toast cheatSheet = Toast.makeText(context, mItemData.getTitle(), Toast.LENGTH_SHORT);
if (midy < displayFrame.height()) {
// Show along the top; follow action buttons
- cheatSheet.setGravity(Gravity.TOP | GravityCompat.END, referenceX, height);
+ cheatSheet.setGravity(Gravity.TOP | GravityCompat.END, referenceX,
+ screenPos[1] + height - displayFrame.top);
} else {
// Show along the bottom center
cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);