RecyclerView in LayoutLib: better XML attrs.

 - RecyclerView now supports XML attributes natively. Thus, remove the
   custom support via tools attribute. Users with older versions of
   RecyclerView should update.
 - Add Context.getPackageName() support used by RecyclerView.
 - Update SessionParamsFlags with the new changes and rename it to
   RenderParamsFlags.

The attribute behaves slightly different from the original tools
attribute. For usage, see commit 044b5b61e96 in frameworks/support.

Change-Id: I12073e37a2ba411558ca1d3e30c399e3d9a0b144
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
index 5db9556..2e649c3 100644
--- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -16,19 +16,15 @@
 
 package android.view;
 
-import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.LayoutlibCallback;
 import com.android.ide.common.rendering.api.MergeCookie;
 import com.android.ide.common.rendering.api.ResourceReference;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.BridgeConstants;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
-import com.android.layoutlib.bridge.android.support.RecyclerViewUtil;
-import com.android.layoutlib.bridge.android.support.RecyclerViewUtil.LayoutManagerType;
 import com.android.layoutlib.bridge.impl.ParserFactory;
-import com.android.layoutlib.bridge.impl.RenderSessionImpl;
 import com.android.resources.ResourceType;
 import com.android.util.Pair;
 
@@ -233,22 +229,6 @@
             if (viewKey != null) {
                 bc.addViewKey(view, viewKey);
             }
-            if (RenderSessionImpl.isInstanceOf(view, RecyclerViewUtil.CN_RECYCLER_VIEW)) {
-                String type = attrs.getAttributeValue(BridgeConstants.NS_RESOURCES,
-                                BridgeConstants.ATTR_LAYOUT_MANAGER_TYPE);
-                if (type != null) {
-                    LayoutManagerType layoutManagerType = LayoutManagerType.getByLogicalName(type);
-                    if (layoutManagerType == null) {
-                        layoutManagerType = LayoutManagerType.getByClassName(type);
-                    }
-                    if (layoutManagerType == null) {
-                        // add the classname itself.
-                        bc.addCookie(view, type);
-                    } else {
-                        bc.addCookie(view, layoutManagerType);
-                    }
-                }
-            }
         }
     }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 3f553e7..2cbbeba 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -92,6 +92,8 @@
 import java.util.List;
 import java.util.Map;
 
+import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE;
+
 /**
  * Custom implementation of Context/Activity to handle non compiled resources.
  */
@@ -305,7 +307,7 @@
         // check if this is a style resource
         if (value instanceof StyleResourceValue) {
             // get the id that will represent this style.
-            outValue.resourceId = getDynamicIdByStyle((StyleResourceValue)value);
+            outValue.resourceId = getDynamicIdByStyle((StyleResourceValue) value);
             return true;
         }
 
@@ -783,6 +785,14 @@
     }
 
 
+    @Override
+    public String getPackageName() {
+        if (mApplicationInfo.packageName == null) {
+            mApplicationInfo.packageName = mLayoutlibCallback.getFlag(FLAG_KEY_APPLICATION_PACKAGE);
+        }
+        return mApplicationInfo.packageName;
+    }
+
     // ------------- private new methods
 
     /**
@@ -1190,12 +1200,6 @@
     }
 
     @Override
-    public String getPackageName() {
-        // pass
-        return null;
-    }
-
-    @Override
     public String getBasePackageName() {
         // pass
         return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
new file mode 100644
index 0000000..b98f96f
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/RenderParamsFlags.java
@@ -0,0 +1,53 @@
+/*
+ * 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.RenderParams;
+import com.android.ide.common.rendering.api.SessionParams.Key;
+
+/**
+ * This contains all known keys for the {@link RenderParams#getFlag(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 RenderParamsFlags {
+
+    public static final Key<String> FLAG_KEY_ROOT_TAG =
+            new Key<String>("rootTag", String.class);
+    public static final Key<Boolean> FLAG_KEY_DISABLE_BITMAP_CACHING =
+            new Key<Boolean>("disableBitmapCaching", Boolean.class);
+    public static final Key<Boolean> FLAG_KEY_RENDER_ALL_DRAWABLE_STATES =
+            new Key<Boolean>("renderAllDrawableStates", Boolean.class);
+    /**
+     * To tell LayoutLib that the IDE supports RecyclerView.
+     * <p/>
+     * Default is false.
+     */
+    public static final Key<Boolean> FLAG_KEY_RECYCLER_VIEW_SUPPORT =
+            new Key<Boolean>("recyclerViewSupport", Boolean.class);
+    /**
+     * The application package name. Used via
+     * {@link com.android.ide.common.rendering.api.LayoutlibCallback#getFlag(Key)}
+     */
+    public static final Key<String> FLAG_KEY_APPLICATION_PACKAGE =
+            new Key<String>("applicationPackage", String.class);
+
+    // Disallow instances.
+    private RenderParamsFlags() {}
+}
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
deleted file mode 100644
index 22b5192..0000000
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/SessionParamsFlags.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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);
-    public static final SessionParams.Key<Boolean> FLAG_KEY_RECYCLER_VIEW_SUPPORT =
-            new SessionParams.Key<Boolean>("recyclerViewSupport", Boolean.class);
-
-    // Disallow instances.
-    private SessionParamsFlags() {}
-}
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 aac5d33..e4c7288 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
@@ -23,15 +23,16 @@
 import com.android.ide.common.rendering.api.SessionParams;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
-import com.android.layoutlib.bridge.android.SessionParamsFlags;
+import com.android.layoutlib.bridge.android.RenderParamsFlags;
 
 import android.content.Context;
 import android.view.View;
-import android.widget.LinearLayout;
 
 import java.lang.reflect.Method;
 
-import static com.android.layoutlib.bridge.util.ReflectionUtils.*;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod;
+import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke;
 
 /**
  * Utility class for working with android.support.v7.widget.RecyclerView
@@ -39,17 +40,15 @@
 @SuppressWarnings("SpellCheckingInspection")  // for "recycler".
 public class RecyclerViewUtil {
 
-    /**
-     * Used by {@link LayoutManagerType}.
-     * <p/>
-     * Not declared inside the enum, since it needs to be accessible in the constructor.
-     */
-    private static final Object CONTEXT = new Object();
-
-    public static final String CN_RECYCLER_VIEW = "android.support.v7.widget.RecyclerView";
+    private static final String RV_PKG_PREFIX = "android.support.v7.widget.";
+    public static final String CN_RECYCLER_VIEW = RV_PKG_PREFIX + "RecyclerView";
     private static final String CN_LAYOUT_MANAGER = CN_RECYCLER_VIEW + "$LayoutManager";
     private static final String CN_ADAPTER = CN_RECYCLER_VIEW + "$Adapter";
 
+    // LinearLayoutManager related constants.
+    private static final String CN_LINEAR_LAYOUT_MANAGER = RV_PKG_PREFIX + "LinearLayoutManager";
+    private static final Class<?>[] LLM_CONSTRUCTOR_SIGNATURE = new Class<?>[]{Context.class};
+
     /**
      * Tries to create an Adapter ({@code android.support.v7.widget.RecyclerView.Adapter} and a
      * LayoutManager {@code RecyclerView.LayoutManager} and assign these to the {@code RecyclerView}
@@ -71,39 +70,35 @@
 
     private static void setLayoutManager(@NonNull View recyclerView, @NonNull BridgeContext context,
             @NonNull LayoutlibCallback callback) throws ReflectionException {
-        Object cookie = context.getCookie(recyclerView);
-        assert cookie == null || cookie instanceof LayoutManagerType || cookie instanceof String;
-        if (!(cookie instanceof LayoutManagerType)) {
-            if (cookie != null) {
-                // TODO: When layoutlib API is updated, try to load the class with a null
-                // constructor or a constructor taking one argument - the context.
-                Bridge.getLog().warning(LayoutLog.TAG_UNSUPPORTED,
-                        "LayoutManager (" + cookie + ") not found, falling back to " +
-                                "LinearLayoutManager", null);
-            }
-            cookie = LayoutManagerType.getDefault();
+        if (getLayoutManager(recyclerView) == null) {
+            // Only set the layout manager if not already set by the recycler view.
+            Object layoutManager = createLayoutManager(context, callback);
+            setProperty(recyclerView, CN_LAYOUT_MANAGER, layoutManager, "setLayoutManager");
         }
-        Object layoutManager = createLayoutManager((LayoutManagerType) cookie, context, callback);
-        setProperty(recyclerView, CN_LAYOUT_MANAGER, layoutManager, "setLayoutManager");
     }
 
+    /** Creates a LinearLayoutManager using the provided context. */
     @Nullable
-    private static Object createLayoutManager(@Nullable LayoutManagerType type,
-            @NonNull Context context, @NonNull LayoutlibCallback callback)
+    private static Object createLayoutManager(@NonNull Context context,
+            @NonNull LayoutlibCallback callback)
             throws ReflectionException {
-        if (type == null) {
-            type = LayoutManagerType.getDefault();
-        }
         try {
-            return callback.loadView(type.getClassName(), type.getSignature(), type.getArgs(context));
+            return callback.loadView(CN_LINEAR_LAYOUT_MANAGER, LLM_CONSTRUCTOR_SIGNATURE,
+                    new Object[]{ context});
         } catch (Exception e) {
             throw new ReflectionException(e);
         }
     }
 
     @Nullable
+    private static Object getLayoutManager(View recyclerview) throws ReflectionException {
+        Method getLayoutManager = getMethod(recyclerview.getClass(), "getLayoutManager");
+        return getLayoutManager != null ? invoke(getLayoutManager, recyclerview) : null;
+    }
+
+    @Nullable
     private static Object createAdapter(@NonNull SessionParams params) throws ReflectionException {
-        Boolean ideSupport = params.getFlag(SessionParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT);
+        Boolean ideSupport = params.getFlag(RenderParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT);
         if (ideSupport != Boolean.TRUE) {
             return null;
         }
@@ -145,74 +140,4 @@
         }
         throw new RuntimeException("invalid object/classname combination.");
     }
-
-    /** Supported LayoutManagers. */
-    public enum LayoutManagerType {
-        LINEAR_LAYOUT_MANGER("Linear",
-                "android.support.v7.widget.LinearLayoutManager",
-                new Class[]{Context.class}, new Object[]{CONTEXT}),
-        GRID_LAYOUT_MANAGER("Grid",
-                "android.support.v7.widget.GridLayoutManager",
-                new Class[]{Context.class, int.class}, new Object[]{CONTEXT, 2}),
-        STAGGERED_GRID_LAYOUT_MANAGER("StaggeredGrid",
-                "android.support.v7.widget.StaggeredGridLayoutManager",
-                new Class[]{int.class, int.class}, new Object[]{2, LinearLayout.VERTICAL});
-
-        private String mLogicalName;
-        private String mClassName;
-        private Class[] mSignature;
-        private Object[] mArgs;
-
-        LayoutManagerType(String logicalName, String className, Class[] signature, Object[] args) {
-            mLogicalName = logicalName;
-            mClassName = className;
-            mSignature = signature;
-            mArgs = args;
-        }
-
-        String getClassName() {
-            return mClassName;
-        }
-
-        Class[] getSignature() {
-            return mSignature;
-        }
-
-        @NonNull
-        Object[] getArgs(Context context) {
-            Object[] args = new Object[mArgs.length];
-            System.arraycopy(mArgs, 0, args, 0, mArgs.length);
-            for (int i = 0; i < args.length; i++) {
-                if (args[i] == CONTEXT) {
-                    args[i] = context;
-                }
-            }
-            return args;
-        }
-
-        @NonNull
-        public static LayoutManagerType getDefault() {
-            return LINEAR_LAYOUT_MANGER;
-        }
-
-        @Nullable
-        public static LayoutManagerType getByLogicalName(@NonNull String logicalName) {
-            for (LayoutManagerType type : values()) {
-                if (logicalName.equals(type.mLogicalName)) {
-                    return type;
-                }
-            }
-            return null;
-        }
-
-        @Nullable
-        public static LayoutManagerType getByClassName(@NonNull String className) {
-            for (LayoutManagerType type : values()) {
-                if (className.equals(type.mClassName)) {
-                    return type;
-                }
-            }
-            return null;
-        }
-    }
 }
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 95576ef..a14fe18 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,7 +45,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.android.RenderParamsFlags;
 import com.android.layoutlib.bridge.android.support.RecyclerViewUtil;
 import com.android.layoutlib.bridge.bars.AppCompatActionBar;
 import com.android.layoutlib.bridge.bars.BridgeActionBar;
@@ -403,7 +403,7 @@
             // it can instantiate the custom Fragment.
             Fragment_Delegate.setLayoutlibCallback(params.getLayoutlibCallback());
 
-            String rootTag = params.getFlag(SessionParamsFlags.FLAG_KEY_ROOT_TAG);
+            String rootTag = params.getFlag(RenderParamsFlags.FLAG_KEY_ROOT_TAG);
             boolean isPreference = "PreferenceScreen".equals(rootTag);
             View view;
             if (isPreference) {