Merge "Add drawable caching"
diff --git a/api/current.txt b/api/current.txt
index 35041d1..3919fb8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -45254,7 +45254,7 @@
method public static boolean allowFileSchemeCookies();
method public abstract void flush();
method public abstract java.lang.String getCookie(java.lang.String);
- method public static synchronized android.webkit.CookieManager getInstance();
+ method public static android.webkit.CookieManager getInstance();
method public abstract boolean hasCookies();
method public abstract deprecated void removeAllCookie();
method public abstract void removeAllCookies(android.webkit.ValueCallback<java.lang.Boolean>);
@@ -45269,8 +45269,8 @@
}
public final deprecated class CookieSyncManager extends android.webkit.WebSyncManager {
- method public static synchronized android.webkit.CookieSyncManager createInstance(android.content.Context);
- method public static synchronized android.webkit.CookieSyncManager getInstance();
+ method public static android.webkit.CookieSyncManager createInstance(android.content.Context);
+ method public static android.webkit.CookieSyncManager getInstance();
method protected deprecated void syncFromRamToFlash();
field protected static final java.lang.String LOGTAG = "websync";
field protected android.webkit.WebViewDatabase mDataBase;
diff --git a/api/system-current.txt b/api/system-current.txt
index 5c6c122..44ee2c9 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -48416,7 +48416,7 @@
method public abstract java.lang.String getCookie(java.lang.String);
method public abstract java.lang.String getCookie(java.lang.String, boolean);
method public synchronized java.lang.String getCookie(android.net.WebAddress);
- method public static synchronized android.webkit.CookieManager getInstance();
+ method public static android.webkit.CookieManager getInstance();
method public abstract boolean hasCookies();
method public abstract boolean hasCookies(boolean);
method public abstract deprecated void removeAllCookie();
@@ -48433,8 +48433,8 @@
}
public final deprecated class CookieSyncManager extends android.webkit.WebSyncManager {
- method public static synchronized android.webkit.CookieSyncManager createInstance(android.content.Context);
- method public static synchronized android.webkit.CookieSyncManager getInstance();
+ method public static android.webkit.CookieSyncManager createInstance(android.content.Context);
+ method public static android.webkit.CookieSyncManager getInstance();
method protected deprecated void syncFromRamToFlash();
field protected static final java.lang.String LOGTAG = "websync";
field protected android.webkit.WebViewDatabase mDataBase;
diff --git a/api/test-current.txt b/api/test-current.txt
index 4d55c29..9e3387f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -45334,7 +45334,7 @@
method public static boolean allowFileSchemeCookies();
method public abstract void flush();
method public abstract java.lang.String getCookie(java.lang.String);
- method public static synchronized android.webkit.CookieManager getInstance();
+ method public static android.webkit.CookieManager getInstance();
method public abstract boolean hasCookies();
method public abstract deprecated void removeAllCookie();
method public abstract void removeAllCookies(android.webkit.ValueCallback<java.lang.Boolean>);
@@ -45349,8 +45349,8 @@
}
public final deprecated class CookieSyncManager extends android.webkit.WebSyncManager {
- method public static synchronized android.webkit.CookieSyncManager createInstance(android.content.Context);
- method public static synchronized android.webkit.CookieSyncManager getInstance();
+ method public static android.webkit.CookieSyncManager createInstance(android.content.Context);
+ method public static android.webkit.CookieSyncManager getInstance();
method protected deprecated void syncFromRamToFlash();
field protected static final java.lang.String LOGTAG = "websync";
field protected android.webkit.WebViewDatabase mDataBase;
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index 3ec235b..67289c2 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -35,7 +35,7 @@
*
* @return the singleton CookieManager instance
*/
- public static synchronized CookieManager getInstance() {
+ public static CookieManager getInstance() {
return WebViewFactory.getProvider().getCookieManager();
}
diff --git a/core/java/android/webkit/CookieSyncManager.java b/core/java/android/webkit/CookieSyncManager.java
index eda8d36..c974b32 100644
--- a/core/java/android/webkit/CookieSyncManager.java
+++ b/core/java/android/webkit/CookieSyncManager.java
@@ -65,6 +65,7 @@
private static CookieSyncManager sRef;
private static boolean sGetInstanceAllowed = false;
+ private static final Object sLock = new Object();
private CookieSyncManager() {
super(null, null);
@@ -77,12 +78,14 @@
*
* @return CookieSyncManager
*/
- public static synchronized CookieSyncManager getInstance() {
- checkInstanceIsAllowed();
- if (sRef == null) {
- sRef = new CookieSyncManager();
+ public static CookieSyncManager getInstance() {
+ synchronized (sLock) {
+ checkInstanceIsAllowed();
+ if (sRef == null) {
+ sRef = new CookieSyncManager();
+ }
+ return sRef;
}
- return sRef;
}
/**
@@ -90,12 +93,14 @@
* @param context
* @return CookieSyncManager
*/
- public static synchronized CookieSyncManager createInstance(Context context) {
- if (context == null) {
- throw new IllegalArgumentException("Invalid context argument");
+ public static CookieSyncManager createInstance(Context context) {
+ synchronized (sLock) {
+ if (context == null) {
+ throw new IllegalArgumentException("Invalid context argument");
+ }
+ setGetInstanceIsAllowed();
+ return getInstance();
}
- setGetInstanceIsAllowed();
- return getInstance();
}
/**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index cbfe43a..6f3fa36 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -2322,7 +2322,7 @@
}
}
- private static synchronized WebViewFactoryProvider getFactory() {
+ private static WebViewFactoryProvider getFactory() {
return WebViewFactory.getProvider();
}
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java b/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java
index 86f813e..3629162 100644
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java
@@ -24,6 +24,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
+import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.media.MediaScannerConnection;
import android.net.Uri;
@@ -50,6 +51,9 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
public class NekoLand extends Activity implements PrefsListener {
public static boolean DEBUG = false;
@@ -98,7 +102,19 @@
cats[i] = Cat.create(this);
}
} else {
- cats = mPrefs.getCats().toArray(new Cat[0]);
+ final float[] hsv = new float[3];
+ List<Cat> list = mPrefs.getCats();
+ Collections.sort(list, new Comparator<Cat>() {
+ @Override
+ public int compare(Cat cat, Cat cat2) {
+ Color.colorToHSV(cat.getBodyColor(), hsv);
+ float bodyH1 = hsv[0];
+ Color.colorToHSV(cat2.getBodyColor(), hsv);
+ float bodyH2 = hsv[0];
+ return Float.compare(bodyH1, bodyH2);
+ }
+ });
+ cats = list.toArray(new Cat[0]);
}
mAdapter.setCats(cats);
return cats.length;
diff --git a/packages/SettingsLib/res/values-lv/arrays.xml b/packages/SettingsLib/res/values-lv/arrays.xml
index fe134db..801de5d 100644
--- a/packages/SettingsLib/res/values-lv/arrays.xml
+++ b/packages/SettingsLib/res/values-lv/arrays.xml
@@ -80,8 +80,18 @@
<item msgid="3606047780792894151">"4 MB vienam žurnāla buferim"</item>
<item msgid="5431354956856655120">"16 MB vienam žurnāla buferim"</item>
</string-array>
- <!-- no translation found for select_logpersist_titles:3 (2850427388488887328) -->
- <!-- no translation found for select_logpersist_summaries:3 (8489661142527693381) -->
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"Izslēgts"</item>
+ <item msgid="3054662377365844197">"Visi"</item>
+ <item msgid="688870735111627832">"Visi, izņemot radio"</item>
+ <item msgid="2850427388488887328">"tikai kodolā"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"Izslēgts"</item>
+ <item msgid="172978079776521897">"Visi reģistra buferi"</item>
+ <item msgid="3873873912383879240">"Visi, izņemot radio reģistra buferus"</item>
+ <item msgid="8489661142527693381">"tikai kodola žurnāla buferī"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Animācija ir izslēgta"</item>
<item msgid="6624864048416710414">"Animācijas mērogs: 0,5x"</item>
diff --git a/packages/SettingsLib/res/values-zh-rCN/arrays.xml b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
index 659007b..8a3febd 100644
--- a/packages/SettingsLib/res/values-zh-rCN/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
@@ -80,8 +80,18 @@
<item msgid="3606047780792894151">"每个日志缓冲区 4M"</item>
<item msgid="5431354956856655120">"每个日志缓冲区 16M"</item>
</string-array>
- <!-- no translation found for select_logpersist_titles:3 (2850427388488887328) -->
- <!-- no translation found for select_logpersist_summaries:3 (8489661142527693381) -->
+ <string-array name="select_logpersist_titles">
+ <item msgid="1744840221860799971">"关闭"</item>
+ <item msgid="3054662377365844197">"全部"</item>
+ <item msgid="688870735111627832">"所有非无线电"</item>
+ <item msgid="2850427388488887328">"仅限内核"</item>
+ </string-array>
+ <string-array name="select_logpersist_summaries">
+ <item msgid="2216470072500521830">"关闭"</item>
+ <item msgid="172978079776521897">"所有日志缓冲区"</item>
+ <item msgid="3873873912383879240">"所有非无线电日志缓冲区"</item>
+ <item msgid="8489661142527693381">"仅限内核日志缓冲区"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"关闭动画"</item>
<item msgid="6624864048416710414">"动画缩放 0.5x"</item>
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
index 53c2fd7..407d315 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
@@ -30,7 +30,7 @@
import com.android.internal.annotations.GuardedBy;
-import libcore.io.IoUtils;
+import libcore.io.IoBridge;
import libcore.util.HexEncoding;
import java.io.FileDescriptor;
@@ -457,7 +457,9 @@
private void closeSocket() {
if (mSocket != null) {
- IoUtils.closeQuietly(mSocket);
+ try {
+ IoBridge.closeAndSignalBlockedThreads(mSocket);
+ } catch (IOException ignored) {}
}
mSocket = null;
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
index 1dfd305..2903e3a4 100644
--- a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
@@ -91,16 +91,14 @@
@LayoutlibDelegate
static long nCreateTree(long rootGroupPtr) {
- VGroup_Delegate rootGroup = VNativeObject.getDelegate(rootGroupPtr);
- return sPathManager.addNewDelegate(new VPathRenderer_Delegate(rootGroup));
+ return sPathManager.addNewDelegate(new VPathRenderer_Delegate(rootGroupPtr));
}
@LayoutlibDelegate
static long nCreateTreeFromCopy(long rendererToCopyPtr, long rootGroupPtr) {
- VGroup_Delegate rootGroup = VNativeObject.getDelegate(rootGroupPtr);
VPathRenderer_Delegate rendererToCopy = VNativeObject.getDelegate(rendererToCopyPtr);
return sPathManager.addNewDelegate(new VPathRenderer_Delegate(rendererToCopy,
- rootGroup));
+ rootGroupPtr));
}
@LayoutlibDelegate
@@ -163,7 +161,6 @@
@LayoutlibDelegate
static long nCreateFullPath(long nativeFullPathPtr) {
VFullPath_Delegate original = VNativeObject.getDelegate(nativeFullPathPtr);
-
return sPathManager.addNewDelegate(new VFullPath_Delegate(original));
}
@@ -247,8 +244,7 @@
@LayoutlibDelegate
static long nCreateGroup(long groupPtr) {
VGroup_Delegate original = VNativeObject.getDelegate(groupPtr);
- return sPathManager.addNewDelegate(
- new VGroup_Delegate(original, new ArrayMap<String, Object>()));
+ return sPathManager.addNewDelegate(new VGroup_Delegate(original, new ArrayMap<>()));
}
@LayoutlibDelegate
@@ -1029,7 +1025,7 @@
private final Path mPath;
private final Path mRenderPath;
private final Matrix mFinalPathMatrix = new Matrix();
- private final VGroup_Delegate mRootGroup;
+ private final long mRootGroupPtr;
private float mViewportWidth = 0;
private float mViewportHeight = 0;
private float mRootAlpha = 1.0f;
@@ -1037,15 +1033,15 @@
private Paint mFillPaint;
private PathMeasure mPathMeasure;
- private VPathRenderer_Delegate(VGroup_Delegate rootGroup) {
- mRootGroup = rootGroup;
+ private VPathRenderer_Delegate(long rootGroupPtr) {
+ mRootGroupPtr = rootGroupPtr;
mPath = new Path();
mRenderPath = new Path();
}
private VPathRenderer_Delegate(VPathRenderer_Delegate rendererToCopy,
- VGroup_Delegate rootGroup) {
- this(rootGroup);
+ long rootGroupPtr) {
+ this(rootGroupPtr);
mViewportWidth = rendererToCopy.mViewportWidth;
mViewportHeight = rendererToCopy.mViewportHeight;
mRootAlpha = rendererToCopy.mRootAlpha;
@@ -1087,7 +1083,7 @@
public void draw(long canvasPtr, long filterPtr, int w, int h) {
// Traverse the tree in pre-order to draw.
- drawGroupTree(mRootGroup, Matrix.IDENTITY_MATRIX, canvasPtr, w, h, filterPtr);
+ drawGroupTree(VNativeObject.getDelegate(mRootGroupPtr), Matrix.IDENTITY_MATRIX, canvasPtr, w, h, filterPtr);
}
private void drawPath(VGroup_Delegate VGroup, VPath_Delegate VPath, long canvasPtr,
@@ -1227,5 +1223,14 @@
@Override
public void setName(String name) {
}
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+
+ // The mRootGroupPtr is not explicitly freed by anything in the VectorDrawable so we
+ // need to free it here.
+ sPathManager.removeJavaReferenceFor(mRootGroupPtr);
+ }
}
}
diff --git a/tools/layoutlib/bridge/src/android/preference/BridgePreferenceInflater.java b/tools/layoutlib/bridge/src/android/preference/BridgePreferenceInflater.java
index 4f00b5d..aa393a9 100644
--- a/tools/layoutlib/bridge/src/android/preference/BridgePreferenceInflater.java
+++ b/tools/layoutlib/bridge/src/android/preference/BridgePreferenceInflater.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.util.AttributeSet;
+import android.view.InflateException;
public class BridgePreferenceInflater extends PreferenceInflater {
@@ -42,7 +43,15 @@
viewKey = ((BridgeXmlBlockParser) attrs).getViewCookie();
}
- Preference preference = super.onCreateItem(name, attrs);
+ Preference preference = null;
+ try {
+ preference = super.onCreateItem(name, attrs);
+ } catch (ClassNotFoundException | InflateException exception) {
+ // name is probably not a valid preference type
+ if ("SwitchPreferenceCompat".equals(name)) {
+ preference = super.onCreateItem("SwitchPreference", attrs);
+ }
+ }
if (viewKey != null && bc != null) {
bc.addCookie(preference, viewKey);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
index d432120..ab27819 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java
@@ -21,6 +21,7 @@
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.RenderParamsFlags;
+import com.android.layoutlib.bridge.util.ReflectionUtils;
import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
import android.annotation.NonNull;
@@ -116,7 +117,7 @@
private static void setProperty(@NonNull Object object, @NonNull String propertyClassName,
@NonNull Object propertyValue, @NonNull String propertySetter)
throws ReflectionException {
- Class<?> propertyClass = getClassInstance(propertyValue, propertyClassName);
+ Class<?> propertyClass = ReflectionUtils.getClassInstance(propertyValue, propertyClassName);
setProperty(object, propertyClass, propertyValue, propertySetter);
}
@@ -126,22 +127,4 @@
invoke(getMethod(object.getClass(), propertySetter, propertyClass), object, propertyValue);
}
- /**
- * Looks through the class hierarchy of {@code object} at runtime and returns the class matching
- * the name {@code className}.
- * <p/>
- * This is used when we cannot use Class.forName() since the class we want was loaded from a
- * different ClassLoader.
- */
- @NonNull
- private static Class<?> getClassInstance(@NonNull Object object, @NonNull String className) {
- Class<?> superClass = object.getClass();
- while (superClass != null) {
- if (className.equals(superClass.getName())) {
- return superClass;
- }
- superClass = superClass.getSuperclass();
- }
- throw new RuntimeException("invalid object/classname combination.");
- }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java
new file mode 100644
index 0000000..0124e83
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/SupportPreferencesUtil.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2016 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 com.android.layoutlib.bridge.android.support;
+
+import com.android.ide.common.rendering.api.LayoutlibCallback;
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.StyleResourceValue;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+import com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.view.ContextThemeWrapper;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+import android.widget.ScrollView;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+
+import static com.android.layoutlib.bridge.util.ReflectionUtils.getClassInstance;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
+
+/**
+ * Class with utility methods to instantiate Preferences provided by the support library.
+ * This class uses reflection to access the support preference objects so it heavily depends on
+ * the API being stable.
+ */
+public class SupportPreferencesUtil {
+ private static final String PREFERENCE_PKG = "android.support.v7.preference";
+ private static final String PREFERENCE_MANAGER = PREFERENCE_PKG + ".PreferenceManager";
+ private static final String PREFERENCE_GROUP = PREFERENCE_PKG + ".PreferenceGroup";
+ private static final String PREFERENCE_GROUP_ADAPTER =
+ PREFERENCE_PKG + ".PreferenceGroupAdapter";
+ private static final String PREFERENCE_INFLATER = PREFERENCE_PKG + ".PreferenceInflater";
+
+ private SupportPreferencesUtil() {
+ }
+
+ @NonNull
+ private static Object instantiateClass(@NonNull LayoutlibCallback callback,
+ @NonNull String className, @Nullable Class[] constructorSignature,
+ @Nullable Object[] constructorArgs) throws ReflectionException {
+ try {
+ Object instance = callback.loadClass(className, constructorSignature, constructorArgs);
+ if (instance == null) {
+ throw new ClassNotFoundException(className + " class not found");
+ }
+ return instance;
+ } catch (ClassNotFoundException e) {
+ throw new ReflectionException(e);
+ }
+ }
+
+ @NonNull
+ private static Object createPreferenceGroupAdapter(@NonNull LayoutlibCallback callback,
+ @NonNull Object preferenceScreen) throws ReflectionException {
+ Class<?> preferenceGroupClass = getClassInstance(preferenceScreen, PREFERENCE_GROUP);
+
+ return instantiateClass(callback, PREFERENCE_GROUP_ADAPTER,
+ new Class[]{preferenceGroupClass}, new Object[]{preferenceScreen});
+ }
+
+ @NonNull
+ private static Object createInflatedPreference(@NonNull LayoutlibCallback callback,
+ @NonNull Context context, @NonNull XmlPullParser parser, @NonNull Object preferenceScreen,
+ @NonNull Object preferenceManager) throws ReflectionException {
+ Class<?> preferenceGroupClass = getClassInstance(preferenceScreen, PREFERENCE_GROUP);
+ Object preferenceInflater = instantiateClass(callback, PREFERENCE_INFLATER,
+ new Class[]{Context.class, preferenceManager.getClass()},
+ new Object[]{context, preferenceManager});
+ Object inflatedPreference = invoke(
+ getMethod(preferenceInflater.getClass(), "inflate", XmlPullParser.class,
+ preferenceGroupClass), preferenceInflater, parser, null);
+
+ if (inflatedPreference == null) {
+ throw new ReflectionException("inflate method returned null");
+ }
+
+ return inflatedPreference;
+ }
+
+ /**
+ * Returns a themed wrapper context of {@link BridgeContext} with the theme specified in
+ * ?attr/preferenceTheme applied to it.
+ */
+ @Nullable
+ private static Context getThemedContext(@NonNull BridgeContext bridgeContext) {
+ RenderResources resources = bridgeContext.getRenderResources();
+ ResourceValue preferenceTheme = resources.findItemInTheme("preferenceTheme", false);
+
+ if (preferenceTheme != null) {
+ // resolve it, if needed.
+ preferenceTheme = resources.resolveResValue(preferenceTheme);
+ }
+ if (preferenceTheme instanceof StyleResourceValue) {
+ int styleId = bridgeContext.getDynamicIdByStyle(((StyleResourceValue) preferenceTheme));
+ if (styleId != 0) {
+ return new ContextThemeWrapper(bridgeContext, styleId);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a {@link LinearLayout} containing all the UI widgets representing the preferences
+ * passed in the group adapter.
+ */
+ @Nullable
+ private static LinearLayout setUpPreferencesListView(@NonNull BridgeContext bridgeContext,
+ @NonNull Context themedContext, @NonNull ArrayList<Object> viewCookie,
+ @NonNull Object preferenceGroupAdapter) throws ReflectionException {
+ // Setup the LinearLayout that will contain the preferences
+ LinearLayout listView = new LinearLayout(themedContext);
+ listView.setOrientation(LinearLayout.VERTICAL);
+ listView.setLayoutParams(
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+
+ if (!viewCookie.isEmpty()) {
+ bridgeContext.addViewKey(listView, viewCookie.get(0));
+ }
+
+ // Get all the preferences and add them to the LinearLayout
+ Integer preferencesCount =
+ (Integer) invoke(getMethod(preferenceGroupAdapter.getClass(), "getItemCount"),
+ preferenceGroupAdapter);
+ if (preferencesCount == null) {
+ return listView;
+ }
+
+ Method getItemId = getMethod(preferenceGroupAdapter.getClass(), "getItemId", int.class);
+ Method getItemViewType =
+ getMethod(preferenceGroupAdapter.getClass(), "getItemViewType", int.class);
+ Method onCreateViewHolder =
+ getMethod(preferenceGroupAdapter.getClass(), "onCreateViewHolder", ViewGroup.class,
+ int.class);
+ for (int i = 0; i < preferencesCount; i++) {
+ Long id = (Long) invoke(getItemId, preferenceGroupAdapter, i);
+ if (id == null) {
+ continue;
+ }
+
+ // Get the type of the preference layout and bind it to a newly created view holder
+ Integer type = (Integer) invoke(getItemViewType, preferenceGroupAdapter, i);
+ Object viewHolder =
+ invoke(onCreateViewHolder, preferenceGroupAdapter, listView, type);
+ if (viewHolder == null) {
+ continue;
+ }
+ invoke(getMethod(preferenceGroupAdapter.getClass(), "onBindViewHolder",
+ viewHolder.getClass(), int.class), preferenceGroupAdapter, viewHolder, i);
+
+ try {
+ // Get the view from the view holder and add it to our layout
+ View itemView =
+ (View) viewHolder.getClass().getField("itemView").get(viewHolder);
+
+ int arrayPosition = id.intValue() - 1; // IDs are 1 based
+ if (arrayPosition >= 0 && arrayPosition < viewCookie.size()) {
+ bridgeContext.addViewKey(itemView, viewCookie.get(arrayPosition));
+ }
+ listView.addView(itemView);
+ } catch (IllegalAccessException | NoSuchFieldException ignored) {
+ }
+ }
+
+ return listView;
+ }
+
+ /**
+ * Inflates a preferences layout using the support library. If the support library is not
+ * available, this method will return null without advancing the parsers.
+ */
+ @Nullable
+ public static View inflatePreference(@NonNull BridgeContext bridgeContext,
+ @NonNull XmlPullParser parser, @Nullable ViewGroup root) {
+ try {
+ LayoutlibCallback callback = bridgeContext.getLayoutlibCallback();
+
+ Context context = getThemedContext(bridgeContext);
+ if (context == null) {
+ // Probably we couldn't find the "preferenceTheme" in the theme
+ return null;
+ }
+
+ // Create PreferenceManager
+ Object preferenceManager =
+ instantiateClass(callback, PREFERENCE_MANAGER, new Class[]{Context.class},
+ new Object[]{context});
+
+ // From this moment on, we can assume that we found the support library and that
+ // nothing should fail
+
+ // Create PreferenceScreen
+ Object preferenceScreen =
+ invoke(getMethod(preferenceManager.getClass(), "createPreferenceScreen",
+ Context.class), preferenceManager, context);
+ if (preferenceScreen == null) {
+ return null;
+ }
+
+ // Setup a parser that stores the list of cookies in the same order as the preferences
+ // are inflated. That way we can later reconstruct the list using the preference id
+ // since they are sequential and start in 1.
+ ArrayList<Object> viewCookie = new ArrayList<>();
+ if (parser instanceof BridgeXmlBlockParser) {
+ // Setup a parser that stores the XmlTag
+ parser = new BridgeXmlBlockParser(parser, null, false) {
+ @Override
+ public Object getViewCookie() {
+ return ((BridgeXmlBlockParser) getParser()).getViewCookie();
+ }
+
+ @Override
+ public int next() throws XmlPullParserException, IOException {
+ int ev = super.next();
+ if (ev == XmlPullParser.START_TAG) {
+ viewCookie.add(this.getViewCookie());
+ }
+
+ return ev;
+ }
+ };
+ }
+
+ // Create the PreferenceInflater
+ Object inflatedPreference =
+ createInflatedPreference(callback, context, parser, preferenceScreen,
+ preferenceManager);
+
+ // Setup the RecyclerView (set adapter and layout manager)
+ Object preferenceGroupAdapter =
+ createPreferenceGroupAdapter(callback, inflatedPreference);
+
+ // Instead of just setting the group adapter as adapter for a RecyclerView, we manually
+ // get all the items and add them to a LinearLayout. This allows us to set the view
+ // cookies so the preferences are correctly linked to their XML.
+ LinearLayout listView = setUpPreferencesListView(bridgeContext, context, viewCookie,
+ preferenceGroupAdapter);
+
+ ScrollView scrollView = new ScrollView(context);
+ scrollView.setLayoutParams(
+ new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+ scrollView.addView(listView);
+
+ if (root != null) {
+ root.addView(scrollView);
+ }
+
+ return scrollView;
+ } catch (ReflectionException e) {
+ return null;
+ }
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
index 0c39026..ea40ba7 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java
@@ -165,5 +165,6 @@
int idx = sDelegates.indexOfValue(reference);
out.printf("[%d] %s\n", sDelegates.keyAt(idx), reference.getClass().getSimpleName());
}
+ out.printf("\nTotal number of objects: %d\n", sJavaReferences.size());
}
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index c890793..feed045 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -45,6 +45,7 @@
import com.android.layoutlib.bridge.android.RenderParamsFlags;
import com.android.layoutlib.bridge.android.graphics.NopCanvas;
import com.android.layoutlib.bridge.android.support.DesignLibUtil;
+import com.android.layoutlib.bridge.android.support.SupportPreferencesUtil;
import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
import com.android.resources.ResourceType;
@@ -326,8 +327,14 @@
boolean isPreference = "PreferenceScreen".equals(rootTag);
View view;
if (isPreference) {
- view = Preference_Delegate.inflatePreference(getContext(), mBlockParser,
+ // First try to use the support library inflater. If something fails, fallback
+ // to the system preference inflater.
+ view = SupportPreferencesUtil.inflatePreference(getContext(), mBlockParser,
mContentRoot);
+ if (view == null) {
+ view = Preference_Delegate.inflatePreference(getContext(), mBlockParser,
+ mContentRoot);
+ }
} else {
view = mInflater.inflate(mBlockParser, mContentRoot);
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
index 7ce27b6..040191e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java
@@ -37,6 +37,15 @@
}
}
+ @NonNull
+ public static Method getAccessibleMethod(@NonNull Class<?> clazz, @NonNull String name,
+ @Nullable Class<?>... params) throws ReflectionException {
+ Method method = getMethod(clazz, name, params);
+ method.setAccessible(true);
+
+ return method;
+ }
+
@Nullable
public static Object invoke(@NonNull Method method, @Nullable Object object,
@Nullable Object... args) throws ReflectionException {
@@ -74,6 +83,25 @@
}
/**
+ * Looks through the class hierarchy of {@code object} at runtime and returns the class matching
+ * the name {@code className}.
+ * <p>
+ * This is used when we cannot use Class.forName() since the class we want was loaded from a
+ * different ClassLoader.
+ */
+ @NonNull
+ public static Class<?> getClassInstance(@NonNull Object object, @NonNull String className) {
+ Class<?> superClass = object.getClass();
+ while (superClass != null) {
+ if (className.equals(superClass.getName())) {
+ return superClass;
+ }
+ superClass = superClass.getSuperclass();
+ }
+ throw new RuntimeException("invalid object/classname combination.");
+ }
+
+ /**
* Wraps all reflection related exceptions. Created since ReflectiveOperationException was
* introduced in 1.7 and we are still on 1.6
*/
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index ba687fe..d160268 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -349,7 +349,11 @@
obj = null;
while(ref.get() != null) {
System.gc();
+ System.runFinalization();
}
+
+ System.gc();
+ System.runFinalization();
}
@AfterClass