am 7081a114: Merge "Add preferences rendering." into lmp-dev

* commit '7081a114d4ee190c746f337057b0f84928c162dd':
  Add preferences rendering.
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 56d5617..0224c73 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -215,7 +215,7 @@
 
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, com.android.internal.R.styleable.Preference, defStyleAttr, defStyleRes);
-        for (int i = a.getIndexCount(); i >= 0; i--) {
+        for (int i = a.getIndexCount() - 1; i >= 0; i--) {
             int attr = a.getIndex(i); 
             switch (attr) {
                 case com.android.internal.R.styleable.Preference_icon:
diff --git a/core/java/android/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java
index ad940c6..0a0e625 100644
--- a/core/java/android/preference/PreferenceManager.java
+++ b/core/java/android/preference/PreferenceManager.java
@@ -156,7 +156,7 @@
      * should be used ANY time a preference will be displayed, since some preference
      * types need an Activity for managed queries.
      */
-    private PreferenceManager(Context context) {
+    /*package*/ PreferenceManager(Context context) {
         init(context);
     }
 
diff --git a/tools/layoutlib/bridge/src/android/preference/BridgePreferenceInflater.java b/tools/layoutlib/bridge/src/android/preference/BridgePreferenceInflater.java
new file mode 100644
index 0000000..c2122f4
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/preference/BridgePreferenceInflater.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 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.preference;
+
+import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import java.util.Map;
+
+public class BridgePreferenceInflater extends PreferenceInflater {
+
+    private final Map<Preference, Object> mViewCookieMap;
+
+    public BridgePreferenceInflater(Context context, PreferenceManager preferenceManager,
+            Map<Preference, Object> viewCookieMap) {
+        super(context, preferenceManager);
+        mViewCookieMap = viewCookieMap;
+    }
+
+    @Override
+    protected Preference onCreateItem(String name, AttributeSet attrs)
+            throws ClassNotFoundException {
+        Object viewKey;
+        if (attrs instanceof BridgeXmlBlockParser) {
+            viewKey = ((BridgeXmlBlockParser) attrs).getViewCookie();
+        } else {
+            viewKey = null;
+        }
+        Preference preference = super.onCreateItem(name, attrs);
+        if (viewKey != null) {
+            mViewCookieMap.put(preference, viewKey);
+        }
+        return preference;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/preference/Preference_Delegate.java b/tools/layoutlib/bridge/src/android/preference/Preference_Delegate.java
new file mode 100644
index 0000000..230fe8c
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/preference/Preference_Delegate.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 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.preference;
+
+import com.android.internal.R;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListView;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Delegate that provides implementation for native methods in {@link Preference}
+ * <p/>
+ * Through the layoutlib_create tool, selected methods of Preference have been replaced by calls to
+ * methods of the same name in this delegate class.
+ */
+public class Preference_Delegate {
+
+    private static final Map<Preference, Object> sViewCookies = new HashMap<Preference, Object>();
+
+    @LayoutlibDelegate
+    /*package*/ static void dispatchSetInitialValue(Preference preference) {
+        // pass.
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static View getView(Preference pref, View convertView, ViewGroup parent) {
+        Context context = pref.getContext();
+        BridgeContext bc = context instanceof BridgeContext ? ((BridgeContext) context) : null;
+        convertView = pref.getView_Original(convertView, parent);
+        Object cookie = sViewCookies.get(pref);
+        if (bc != null && cookie != null) {
+            bc.addViewKey(convertView, cookie);
+        }
+        return convertView;
+    }
+
+    /**
+     * Inflates the parser and returns the ListView containing the Preferences. The caller must call
+     * {@link #clearCookiesMap()} when the rendering is complete.
+     */
+    public static View inflatePreference(Context context, XmlPullParser parser, ViewGroup root) {
+        assert sViewCookies.isEmpty();
+        PreferenceManager pm = new PreferenceManager(context);
+        PreferenceScreen ps = pm.getPreferenceScreen();
+        PreferenceInflater inflater = new BridgePreferenceInflater(context, pm, sViewCookies);
+        ps = (PreferenceScreen) inflater.inflate(parser, ps, true);
+        ListView preferenceView = createContainerView(context, root);
+        ps.bind(preferenceView);
+        return preferenceView;
+    }
+
+    private static ListView createContainerView(Context context, ViewGroup root) {
+        TypedArray a = context.obtainStyledAttributes(null, R.styleable.PreferenceFragment,
+                R.attr.preferenceFragmentStyle, 0);
+        int mLayoutResId = a.getResourceId(R.styleable.PreferenceFragment_layout,
+                        R.layout.preference_list_fragment);
+        a.recycle();
+
+        LayoutInflater inflater =
+                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        inflater.inflate(mLayoutResId, root, true);
+
+        return (ListView) root.findViewById(android.R.id.list);
+    }
+
+    public static void clearCookiesMap() {
+        sViewCookies.clear();
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
index 5a467b2..b0d79a8 100644
--- a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
@@ -13,8 +13,8 @@
 
 /**
  * Delegate that provides implementation for native methods in {@link android.text.StaticLayout}
- *
- * Through the layoutlib_create tool, selected methods of Handler have been replaced
+ * <p/>
+ * Through the layoutlib_create tool, selected methods of StaticLayout have been replaced
  * by calls to methods of the same name in this delegate class.
  *
  */
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 0af04ec..4d2c2fc 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -22,6 +22,7 @@
 import com.android.annotations.NonNull;
 import com.android.ide.common.rendering.api.Capability;
 import com.android.ide.common.rendering.api.DrawableParams;
+import com.android.ide.common.rendering.api.Features;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.ide.common.rendering.api.RenderSession;
 import com.android.ide.common.rendering.api.Result;
@@ -180,7 +181,7 @@
      */
     private static LayoutLog sCurrentLog = sDefaultLog;
 
-    private EnumSet<Capability> mCapabilities;
+    private static final int LAST_SUPPORTED_FEATURE = Features.PREFERENCES_RENDERING;
 
     @Override
     public int getApiLevel() {
@@ -188,8 +189,16 @@
     }
 
     @Override
+    @Deprecated
     public EnumSet<Capability> getCapabilities() {
-        return mCapabilities;
+        // The Capability class is deprecated and frozen. All Capabilities enumerated there are
+        // supported by this version of LayoutLibrary. So, it's safe to use EnumSet.allOf()
+        return EnumSet.allOf(Capability.class);
+    }
+
+    @Override
+    public boolean supports(int feature) {
+        return feature <= LAST_SUPPORTED_FEATURE;
     }
 
     @Override
@@ -200,26 +209,6 @@
         sPlatformProperties = platformProperties;
         sEnumValueMap = enumValueMap;
 
-        // don't use EnumSet.allOf(), because the bridge doesn't come with its specific version
-        // of layoutlib_api. It is provided by the client which could have a more recent version
-        // with newer, unsupported capabilities.
-        mCapabilities = EnumSet.of(
-                Capability.UNBOUND_RENDERING,
-                Capability.CUSTOM_BACKGROUND_COLOR,
-                Capability.RENDER,
-                Capability.LAYOUT_ONLY,
-                Capability.EMBEDDED_LAYOUT,
-                Capability.VIEW_MANIPULATION,
-                Capability.PLAY_ANIMATION,
-                Capability.ANIMATED_VIEW_MANIPULATION,
-                Capability.ADAPTER_BINDING,
-                Capability.EXTENDED_VIEWINFO,
-                Capability.FIXED_SCALABLE_NINE_PATCH,
-                Capability.RTL,
-                Capability.ACTION_BAR,
-                Capability.SIMULATE_PLATFORM);
-
-
         BridgeAssetManager.initSystem();
 
         // When DEBUG_LAYOUT is set and is not 0 or false, setup a default listener
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/SessionParamsFlags.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/SessionParamsFlags.java
new file mode 100644
index 0000000..e00ea6a
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/SessionParamsFlags.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014 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;
+
+import com.android.ide.common.rendering.api.SessionParams;
+
+/**
+ * This contains all known keys for the {@link SessionParams#getFlag(SessionParams.Key)}.
+ * <p/>
+ * The IDE has its own copy of this class which may be newer or older than this one.
+ * <p/>
+ * Constants should never be modified or removed from this class.
+ */
+public final class SessionParamsFlags {
+
+    public static final SessionParams.Key<String> FLAG_KEY_ROOT_TAG =
+            new SessionParams.Key<String>("rootTag", String.class);
+
+    // Disallow instances.
+    private SessionParamsFlags() {}
+}
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 a2eed9a..c8e8ba7 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
@@ -49,6 +49,7 @@
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+import com.android.layoutlib.bridge.android.SessionParamsFlags;
 import com.android.layoutlib.bridge.bars.Config;
 import com.android.layoutlib.bridge.bars.NavigationBar;
 import com.android.layoutlib.bridge.bars.StatusBar;
@@ -73,6 +74,7 @@
 import android.graphics.Bitmap_Delegate;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
+import android.preference.Preference_Delegate;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.view.AttachInfo_Accessor;
@@ -87,7 +89,6 @@
 import android.view.ViewGroup.MarginLayoutParams;
 import android.view.ViewParent;
 import android.view.WindowManagerGlobal_Delegate;
-import android.view.ViewParent;
 import android.widget.AbsListView;
 import android.widget.AbsSpinner;
 import android.widget.ActionMenuView;
@@ -397,7 +398,15 @@
             // it can instantiate the custom Fragment.
             Fragment_Delegate.setProjectCallback(params.getProjectCallback());
 
-            View view = mInflater.inflate(mBlockParser, mContentRoot);
+            String rootTag = params.getFlag(SessionParamsFlags.FLAG_KEY_ROOT_TAG);
+            boolean isPreference = "PreferenceScreen".equals(rootTag);
+            View view;
+            if (isPreference) {
+                view = Preference_Delegate.inflatePreference(getContext(), mBlockParser,
+                  mContentRoot);
+            } else {
+                view = mInflater.inflate(mBlockParser, mContentRoot);
+            }
 
             // done with the parser, pop it.
             context.popParser();
@@ -408,7 +417,7 @@
             AttachInfo_Accessor.setAttachInfo(mViewRoot);
 
             // post-inflate process. For now this supports TabHost/TabWidget
-            postInflateProcess(view, params.getProjectCallback());
+            postInflateProcess(view, params.getProjectCallback(), isPreference ? view : null);
 
             // get the background drawable
             if (mWindowBackground != null) {
@@ -590,6 +599,9 @@
             mSystemViewInfoList = visitAllChildren(mViewRoot, 0, params.getExtendedViewInfoMode(),
                     false);
 
+            // clear the preferences cookie map.
+            Preference_Delegate.clearCookiesMap();
+
             // success!
             return SUCCESS.createResult();
         } catch (Throwable e) {
@@ -1210,12 +1222,16 @@
      * based on the content of the {@link FrameLayout}.
      * @param view the root view to process.
      * @param projectCallback callback to the project.
+     * @param skip the view and it's children are not processed.
      */
     @SuppressWarnings("deprecation")  // For the use of Pair
-    private void postInflateProcess(View view, IProjectCallback projectCallback)
+    private void postInflateProcess(View view, IProjectCallback projectCallback, View skip)
             throws PostInflateException {
+        if (view == skip) {
+            return;
+        }
         if (view instanceof TabHost) {
-            setupTabHost((TabHost)view, projectCallback);
+            setupTabHost((TabHost) view, projectCallback);
         } else if (view instanceof QuickContactBadge) {
             QuickContactBadge badge = (QuickContactBadge) view;
             badge.setImageToDefault();
@@ -1248,7 +1264,7 @@
                             boolean skipCallbackParser = false;
 
                             int count = binding.getHeaderCount();
-                            for (int i = 0 ; i < count ; i++) {
+                            for (int i = 0; i < count; i++) {
                                 Pair<View, Boolean> pair = context.inflateView(
                                         binding.getHeaderAt(i),
                                         list, false /*attachToRoot*/, skipCallbackParser);
@@ -1260,7 +1276,7 @@
                             }
 
                             count = binding.getFooterCount();
-                            for (int i = 0 ; i < count ; i++) {
+                            for (int i = 0; i < count; i++) {
                                 Pair<View, Boolean> pair = context.inflateView(
                                         binding.getFooterAt(i),
                                         list, false /*attachToRoot*/, skipCallbackParser);
@@ -1289,11 +1305,11 @@
                 }
             }
         } else if (view instanceof ViewGroup) {
-            ViewGroup group = (ViewGroup)view;
+            ViewGroup group = (ViewGroup) view;
             final int count = group.getChildCount();
-            for (int c = 0 ; c < count ; c++) {
+            for (int c = 0; c < count; c++) {
                 View child = group.getChildAt(c);
-                postInflateProcess(child, projectCallback);
+                postInflateProcess(child, projectCallback, skip);
             }
         }
     }
@@ -1361,6 +1377,7 @@
             for (int i = 0 ; i < count ; i++) {
                 View child = content.getChildAt(i);
                 String tabSpec = String.format("tab_spec%d", i+1);
+                @SuppressWarnings("ConstantConditions")  // child cannot be null.
                 int id = child.getId();
                 @SuppressWarnings("deprecation")
                 Pair<ResourceType, String> resource = projectCallback.resolveResourceId(id);
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 500c338..2f04b11 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -152,6 +152,8 @@
         "android.graphics.Typeface#getSystemFontConfigLocation",
         "android.os.Handler#sendMessageAtTime",
         "android.os.HandlerThread#run",
+        "android.preference.Preference#dispatchSetInitialValue",
+        "android.preference.Preference#getView",
         "android.text.format.DateFormat#is24HourFormat",
         "android.util.Xml#newPullParser",
         "android.view.Choreographer#getRefreshRate",
@@ -273,6 +275,7 @@
 
     private final static String[] EXCLUDED_CLASSES =
         new String[] {
+            "android.preference.PreferenceActivity",
             "org.kxml2.io.KXmlParser"
         };
 
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
index cd3c39e..fa570c8 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
@@ -108,6 +108,7 @@
                         "android.graphics.drawable.*",
                         "android.content.*",
                         "android.content.res.*",
+                        "android.preference.*",
                         "org.apache.harmony.xml.*",
                         "com.android.internal.R**",
                         "android.pim.*", // for datepicker