Merge "More tests for background tinting of AppCompatTextView" into mnc-ub-dev
diff --git a/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java b/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java
index 89be82e..f62a11f 100644
--- a/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java
+++ b/v13/java/android/support/v13/app/FragmentStatePagerAdapter.java
@@ -127,7 +127,7 @@
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
- Fragment fragment = (Fragment)object;
+ Fragment fragment = (Fragment) object;
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
@@ -137,7 +137,8 @@
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
- mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment));
+ mSavedState.set(position, fragment.isAdded()
+ ? mFragmentManager.saveFragmentInstanceState(fragment) : null);
mFragments.set(position, null);
mCurTransaction.remove(fragment);
diff --git a/v4/java/android/support/v4/app/FragmentStatePagerAdapter.java b/v4/java/android/support/v4/app/FragmentStatePagerAdapter.java
index 0c3c6c5..ad9072b 100644
--- a/v4/java/android/support/v4/app/FragmentStatePagerAdapter.java
+++ b/v4/java/android/support/v4/app/FragmentStatePagerAdapter.java
@@ -16,8 +16,6 @@
package android.support.v4.app;
-import java.util.ArrayList;
-
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.view.PagerAdapter;
@@ -25,6 +23,8 @@
import android.view.View;
import android.view.ViewGroup;
+import java.util.ArrayList;
+
/**
* Implementation of {@link android.support.v4.view.PagerAdapter} that
* uses a {@link Fragment} to manage each page. This class also handles
@@ -123,7 +123,7 @@
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
- Fragment fragment = (Fragment)object;
+ Fragment fragment = (Fragment) object;
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
@@ -133,7 +133,8 @@
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
- mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment));
+ mSavedState.set(position, fragment.isAdded()
+ ? mFragmentManager.saveFragmentInstanceState(fragment) : null);
mFragments.set(position, null);
mCurTransaction.remove(fragment);
diff --git a/v4/java/android/support/v4/view/ViewPager.java b/v4/java/android/support/v4/view/ViewPager.java
index 11acb79..ed99227 100644
--- a/v4/java/android/support/v4/view/ViewPager.java
+++ b/v4/java/android/support/v4/view/ViewPager.java
@@ -978,9 +978,7 @@
void populate(int newCurrentItem) {
ItemInfo oldCurInfo = null;
- int focusDirection = View.FOCUS_FORWARD;
if (mCurItem != newCurrentItem) {
- focusDirection = mCurItem < newCurrentItem ? View.FOCUS_RIGHT : View.FOCUS_LEFT;
oldCurInfo = infoForPosition(mCurItem);
mCurItem = newCurrentItem;
}
@@ -1155,7 +1153,7 @@
View child = getChildAt(i);
ii = infoForChild(child);
if (ii != null && ii.position == mCurItem) {
- if (child.requestFocus(focusDirection)) {
+ if (child.requestFocus(View.FOCUS_FORWARD)) {
break;
}
}
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java b/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
index 322bf81..e79f4e3 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatDrawableManager.java
@@ -26,6 +26,7 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Drawable.ConstantState;
import android.graphics.drawable.DrawableContainer;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.InsetDrawable;
@@ -40,6 +41,7 @@
import android.support.v4.graphics.ColorUtils;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v4.util.ArrayMap;
+import android.support.v4.util.LongSparseArray;
import android.support.v4.util.LruCache;
import android.support.v7.appcompat.R;
import android.util.AttributeSet;
@@ -48,7 +50,7 @@
import android.util.TypedValue;
import android.util.Xml;
-import java.lang.reflect.Type;
+import java.lang.ref.WeakReference;
import java.util.WeakHashMap;
import static android.support.v7.widget.ThemeUtils.getDisabledThemeAttrColor;
@@ -60,25 +62,12 @@
*/
public final class AppCompatDrawableManager {
- public interface InflateDelegate {
+ private interface InflateDelegate {
Drawable createFromXmlInner(@NonNull Resources r, @NonNull XmlPullParser parser,
@NonNull AttributeSet attrs, @Nullable Resources.Theme theme);
}
- private static final InflateDelegate VDC_DELEGATE = new InflateDelegate() {
- @Override
- public Drawable createFromXmlInner(@NonNull Resources r, @NonNull XmlPullParser parser,
- @NonNull AttributeSet attrs, @Nullable Resources.Theme theme) {
- try {
- return VectorDrawableCompat.createFromXmlInner(r, parser, attrs, theme);
- } catch (Exception e) {
- Log.e("VdcInflateDelegate", "Exception while inflating <vector>", e);
- return null;
- }
- }
- };
-
- private static final String TAG = "TintManager";
+ private static final String TAG = "AppCompatDrawableManager";
private static final boolean DEBUG = false;
private static final PorterDuff.Mode DEFAULT_MODE = PorterDuff.Mode.SRC_IN;
private static final String SKIP_DRAWABLE_TAG = "appcompat_skip_skip";
@@ -88,7 +77,12 @@
public static AppCompatDrawableManager get() {
if (INSTANCE == null) {
INSTANCE = new AppCompatDrawableManager();
- INSTANCE.addDelegate("vector", VDC_DELEGATE);
+
+ if (Build.VERSION.SDK_INT < 21) {
+ // We only want to use the automatic VectorDrawableCompat handling where it's
+ // needed: on devices running before Lollipop
+ INSTANCE.addDelegate("vector", new VdcInflateDelegate());
+ }
}
return INSTANCE;
}
@@ -159,6 +153,10 @@
private ArrayMap<String, InflateDelegate> mDelegates;
private SparseArray<String> mKnownDrawableIdTags;
+ private final Object mDelegateDrawableCacheLock = new Object();
+ private final WeakHashMap<Context, LongSparseArray<WeakReference<Drawable.ConstantState>>>
+ mDelegateDrawableCaches = new WeakHashMap<>(0);
+
private TypedValue mTypedValue;
public Drawable getDrawable(@NonNull Context context, @DrawableRes int resId) {
@@ -229,16 +227,14 @@
private Drawable loadDrawableFromDelegates(@NonNull Context context, @DrawableRes int resId) {
if (mDelegates != null && !mDelegates.isEmpty()) {
- String cachedTagName = null;
-
if (mKnownDrawableIdTags != null) {
- cachedTagName = mKnownDrawableIdTags.get(resId);
+ final String cachedTagName = mKnownDrawableIdTags.get(resId);
if (SKIP_DRAWABLE_TAG.equals(cachedTagName)
|| (cachedTagName != null && mDelegates.get(cachedTagName) == null)) {
// If we don't have a delegate for the drawable tag, or we've been set to
// skip it, fail fast and return null
if (DEBUG) {
- Log.d(TAG, "loadDrawableFromDelegates. Skipping drawable "
+ Log.d(TAG, "[loadDrawableFromDelegates] Skipping drawable: "
+ context.getResources().getResourceName(resId));
}
return null;
@@ -256,6 +252,18 @@
final Resources res = context.getResources();
res.getValue(resId, tv, true);
+ final long key = (((long) tv.assetCookie) << 32) | tv.data;
+
+ Drawable dr = getCachedDelegateDrawable(context, key);
+ if (dr != null) {
+ if (DEBUG) {
+ Log.i(TAG, "[loadDrawableFromDelegates] Returning cached drawable: " +
+ context.getResources().getResourceName(resId));
+ }
+ // We have a cached drawable, return it!
+ return dr;
+ }
+
if (tv.string != null && tv.string.toString().endsWith(".xml")) {
// If the resource is an XML file, let's try and parse it
try {
@@ -271,28 +279,78 @@
}
final String tagName = parser.getName();
- if (cachedTagName == null) {
- // If we don't already have this cached, add it to the cache
- mKnownDrawableIdTags.append(resId, tagName);
- }
+ // Add the tag name to the cache
+ mKnownDrawableIdTags.append(resId, tagName);
// Now try and find a delegate for the tag name and inflate if found
final InflateDelegate delegate = mDelegates.get(tagName);
if (delegate != null) {
- return delegate.createFromXmlInner(res, parser, attrs, context.getTheme());
+ dr = delegate.createFromXmlInner(res, parser, attrs, context.getTheme());
+ }
+ if (dr != null) {
+ // Add it to the drawable cache
+ dr.setChangingConfigurations(tv.changingConfigurations);
+ if (addCachedDelegateDrawable(context, key, dr) && DEBUG) {
+ Log.i(TAG, "[loadDrawableFromDelegates] Saved drawable to cache: " +
+ context.getResources().getResourceName(resId));
+ }
}
} catch (Exception e) {
Log.e(TAG, "Exception while inflating drawable", e);
}
}
+ if (dr == null) {
+ // If we reach here then the delegate inflation of the resource failed. Mark it as
+ // bad so we skip the id next time
+ mKnownDrawableIdTags.append(resId, SKIP_DRAWABLE_TAG);
+ }
+ return dr;
}
- // If we reach here then the delegate inflation of the resource failed. Mark it as
- // bad so we skip the id next time
- mKnownDrawableIdTags.append(resId, SKIP_DRAWABLE_TAG);
return null;
}
+ private Drawable getCachedDelegateDrawable(@NonNull final Context context, final long key) {
+ synchronized (mDelegateDrawableCacheLock) {
+ final LongSparseArray<WeakReference<ConstantState>> cache
+ = mDelegateDrawableCaches.get(context);
+ if (cache == null) {
+ return null;
+ }
+
+ final WeakReference<ConstantState> wr = cache.get(key);
+ if (wr != null) {
+ // We have the key, and the secret
+ ConstantState entry = wr.get();
+ if (entry != null) {
+ return entry.newDrawable(context.getResources());
+ } else {
+ // Our entry has been purged
+ cache.delete(key);
+ }
+ }
+ }
+ return null;
+ }
+
+ private boolean addCachedDelegateDrawable(@NonNull final Context context, final long key,
+ @NonNull final Drawable drawable) {
+ final ConstantState cs = drawable.getConstantState();
+ if (cs != null) {
+ synchronized (mDelegateDrawableCacheLock) {
+ LongSparseArray<WeakReference<ConstantState>> cache
+ = mDelegateDrawableCaches.get(context);
+ if (cache == null) {
+ cache = new LongSparseArray<>();
+ mDelegateDrawableCaches.put(context, cache);
+ }
+ cache.put(key, new WeakReference<ConstantState>(cs));
+ }
+ return true;
+ }
+ return false;
+ }
+
public final Drawable onDrawableLoadedFromResources(@NonNull Context context,
@NonNull TintResources resources, @DrawableRes final int resId) {
Drawable drawable = loadDrawableFromDelegates(context, resId);
@@ -341,7 +399,8 @@
}
if (DEBUG) {
- Log.d(TAG, "Tinted Drawable: " + context.getResources().getResourceName(resId) +
+ Log.d(TAG, "[tintDrawableUsingColorFilter] Tinted "
+ + context.getResources().getResourceName(resId) +
" with color: #" + Integer.toHexString(color));
}
return true;
@@ -349,14 +408,14 @@
return false;
}
- public final void addDelegate(@NonNull String tagName, @NonNull InflateDelegate delegate) {
+ private void addDelegate(@NonNull String tagName, @NonNull InflateDelegate delegate) {
if (mDelegates == null) {
mDelegates = new ArrayMap<>();
}
mDelegates.put(tagName, delegate);
}
- public final void removeDelegate(@NonNull String tagName, @NonNull InflateDelegate delegate) {
+ private void removeDelegate(@NonNull String tagName, @NonNull InflateDelegate delegate) {
if (mDelegates != null && mDelegates.get(tagName) == delegate) {
mDelegates.remove(tagName);
}
@@ -730,7 +789,7 @@
return Build.VERSION.SDK_INT >= 14;
} else if (drawable instanceof DrawableContainer) {
// If we have a DrawableContainer, let's traverse it's child array
- final Drawable.ConstantState state = drawable.getConstantState();
+ final ConstantState state = drawable.getConstantState();
if (state instanceof DrawableContainer.DrawableContainerState) {
final DrawableContainer.DrawableContainerState containerState =
(DrawableContainer.DrawableContainerState) state;
@@ -772,4 +831,17 @@
}
d.setColorFilter(getPorterDuffColorFilter(color, mode == null ? DEFAULT_MODE : mode));
}
+
+ private static class VdcInflateDelegate implements InflateDelegate {
+ @Override
+ public Drawable createFromXmlInner(@NonNull Resources r, @NonNull XmlPullParser parser,
+ @NonNull AttributeSet attrs, @Nullable Resources.Theme theme) {
+ try {
+ return VectorDrawableCompat.createFromXmlInner(r, parser, attrs, theme);
+ } catch (Exception e) {
+ Log.e("VdcInflateDelegate", "Exception while inflating <vector>", e);
+ return null;
+ }
+ }
+ }
}
diff --git a/v7/appcompat/src/android/support/v7/widget/TintContextWrapper.java b/v7/appcompat/src/android/support/v7/widget/TintContextWrapper.java
index 2503fe4..edbd3fa 100644
--- a/v7/appcompat/src/android/support/v7/widget/TintContextWrapper.java
+++ b/v7/appcompat/src/android/support/v7/widget/TintContextWrapper.java
@@ -19,19 +19,36 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
/**
* A {@link android.content.ContextWrapper} which returns a tint-aware
* {@link android.content.res.Resources} instance from {@link #getResources()}.
*/
class TintContextWrapper extends ContextWrapper {
- public static Context wrap(Context context) {
+ private static final ArrayList<WeakReference<TintContextWrapper>> sCache = new ArrayList<>();
+
+ public static Context wrap(@NonNull final Context context) {
if (!(context instanceof TintContextWrapper)) {
- context = new TintContextWrapper(context);
+ // First check our instance cache
+ for (int i = 0, count = sCache.size(); i < count; i++) {
+ final WeakReference<TintContextWrapper> ref = sCache.get(i);
+ final TintContextWrapper wrapper = ref != null ? ref.get() : null;
+ if (wrapper != null && wrapper.getBaseContext() == context) {
+ return wrapper;
+ }
+ }
+ // If we reach here then the cache didn't have a hit, so create a new instance
+ // and add it to the cahce
+ final TintContextWrapper wrapper = new TintContextWrapper(context);
+ sCache.add(new WeakReference<TintContextWrapper>(wrapper));
+ return wrapper;
}
+
return context;
}