Layoutlib: Load fragments

This change does the following:
- Make the bridge context extend Activity instead of Context
so that it can act as a view factory. This is needed because
the Activity is used as factory for Fragment objects.

- Override the default Fragment.instantiate(...) method
through a delegate. This is done to load the Fragment
classes located in the project (similar to custom views).

Change-Id: If62e7c9ff2b7585677077ad825aa1c3591d1b5e0
diff --git a/bridge/src/android/app/Fragment_Delegate.java b/bridge/src/android/app/Fragment_Delegate.java
new file mode 100644
index 0000000..b46d95c
--- /dev/null
+++ b/bridge/src/android/app/Fragment_Delegate.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2010 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.app;
+
+import com.android.layoutlib.api.IProjectCallback;
+
+import android.content.Context;
+import android.os.Bundle;
+
+/**
+ * Delegate used to provide new implementation of a select few methods of {@link Fragment}
+ *
+ * Through the layoutlib_create tool, the original  methods of Fragment have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * The methods being re-implemented are the ones responsible for instantiating Fragment objects.
+ * Because the classes of these objects are found in the project, these methods need access to
+ * {@link IProjectCallback} object. They are however static methods, so the callback is set
+ * before the inflation through {@link #setProjectCallback(IProjectCallback)}.
+ */
+public class Fragment_Delegate {
+
+    private static IProjectCallback sProjectCallback;
+
+    /**
+     * Sets the current {@link IProjectCallback} to be used to instantiate classes coming
+     * from the project being rendered.
+     */
+    public static void setProjectCallback(IProjectCallback projectCallback) {
+        sProjectCallback = projectCallback;
+    }
+
+    /**
+     * Like {@link #instantiate(Context, String, Bundle)} but with a null
+     * argument Bundle.
+     */
+    /*package*/ static Fragment instantiate(Context context, String fname) {
+        return instantiate(context, fname, null);
+    }
+
+    /**
+     * Create a new instance of a Fragment with the given class name.  This is
+     * the same as calling its empty constructor.
+     *
+     * @param context The calling context being used to instantiate the fragment.
+     * This is currently just used to get its ClassLoader.
+     * @param fname The class name of the fragment to instantiate.
+     * @param args Bundle of arguments to supply to the fragment, which it
+     * can retrieve with {@link #getArguments()}.  May be null.
+     * @return Returns a new fragment instance.
+     * @throws InstantiationException If there is a failure in instantiating
+     * the given fragment class.  This is a runtime exception; it is not
+     * normally expected to happen.
+     */
+    /*package*/ static Fragment instantiate(Context context, String fname, Bundle args) {
+        try {
+            if (sProjectCallback != null) {
+                Fragment f = (Fragment) sProjectCallback.loadView(fname,
+                        new Class[0], new Object[0]);
+
+                if (args != null) {
+                    args.setClassLoader(f.getClass().getClassLoader());
+                    f.mArguments = args;
+                }
+                return f;
+            }
+
+            return null;
+        } catch (ClassNotFoundException e) {
+            throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname
+                    + ": make sure class name exists, is public, and has an"
+                    + " empty constructor that is public", e);
+        } catch (java.lang.InstantiationException e) {
+            throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname
+                    + ": make sure class name exists, is public, and has an"
+                    + " empty constructor that is public", e);
+        } catch (IllegalAccessException e) {
+            throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname
+                    + ": make sure class name exists, is public, and has an"
+                    + " empty constructor that is public", e);
+        } catch (Exception e) {
+            throw new Fragment.InstantiationException("Unable to instantiate fragment " + fname
+                    + ": make sure class name exists, is public, and has an"
+                    + " empty constructor that is public", e);
+        }
+    }
+}
diff --git a/bridge/src/com/android/layoutlib/bridge/Bridge.java b/bridge/src/com/android/layoutlib/bridge/Bridge.java
index bd00b88..996a942 100644
--- a/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -31,6 +31,7 @@
 import com.android.tools.layoutlib.create.MethodAdapter;
 import com.android.tools.layoutlib.create.OverrideMethod;
 
+import android.app.Fragment_Delegate;
 import android.content.ClipData;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
@@ -366,6 +367,12 @@
 
         BridgeContext context = null;
         try {
+            // we need to make sure the Looper has been initialized for this thread.
+            // this is required for View that creates Handler objects.
+            if (Looper.myLooper() == null) {
+                Looper.prepare();
+            }
+
             // setup the display Metrics.
             DisplayMetrics metrics = new DisplayMetrics();
             metrics.densityDpi = density;
@@ -380,6 +387,7 @@
                     frameworkResources, styleParentMap, customViewLoader, logger);
             BridgeInflater inflater = new BridgeInflater(context, customViewLoader);
             context.setBridgeInflater(inflater);
+            inflater.setFactory2(context);
 
             IResourceValue windowBackground = null;
             int screenOffset = 0;
@@ -390,22 +398,22 @@
                 screenOffset = getScreenOffset(frameworkResources, currentTheme, context);
             }
 
-            // we need to make sure the Looper has been initialized for this thread.
-            // this is required for View that creates Handler objects.
-            if (Looper.myLooper() == null) {
-                Looper.prepare();
-            }
-
             BridgeXmlBlockParser parser = new BridgeXmlBlockParser(layoutDescription,
                     context, false /* platformResourceFlag */);
 
             ViewGroup root = new FrameLayout(context);
 
+            // Sets the project callback (custom view loader) to the fragment delegate so that
+            // it can instantiate the custom Fragment.
+            Fragment_Delegate.setProjectCallback(customViewLoader);
+
             View view = inflater.inflate(parser, root);
 
             // post-inflate process. For now this supports TabHost/TabWidget
             postInflateProcess(view, customViewLoader);
 
+            Fragment_Delegate.setProjectCallback(null);
+
             // set the AttachInfo on the root view.
             AttachInfo info = new AttachInfo(new WindowSession(), new Window(),
                     new Handler(), null);
diff --git a/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
index 8592731..b9899b2 100644
--- a/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
+++ b/bridge/src/com/android/layoutlib/bridge/BridgeContext.java
@@ -21,6 +21,8 @@
 import com.android.layoutlib.api.IResourceValue;
 import com.android.layoutlib.api.IStyleResourceValue;
 
+import android.app.Activity;
+import android.app.Fragment;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -49,6 +51,7 @@
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.view.BridgeInflater;
+import android.view.LayoutInflater;
 import android.view.View;
 
 import java.io.File;
@@ -65,7 +68,7 @@
 /**
  * Custom implementation of Context to handle non compiled resources.
  */
-public final class BridgeContext extends Context {
+public final class BridgeContext extends Activity {
 
     private final Resources mResources;
     private final Theme mTheme;
@@ -129,6 +132,9 @@
         mProjectResources = projectResources;
         mFrameworkResources = frameworkResources;
         mStyleInheritanceMap = styleInheritanceMap;
+
+        mFragments.mCurState = Fragment.CREATED;
+        mFragments.mActivity = this;
     }
 
     public void setBridgeInflater(BridgeInflater inflater) {
@@ -155,6 +161,14 @@
         return mLogger;
     }
 
+
+    // ------------- Activity Methods
+
+    @Override
+    public LayoutInflater getLayoutInflater() {
+        return mInflater;
+    }
+
     // ------------ Context methods
 
     @Override
diff --git a/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 8e05c31..1c95400 100644
--- a/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -94,6 +94,7 @@
      * The list of methods to rewrite as delegates.
      */
     private final static String[] DELEGATE_METHODS = new String[] {
+        "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
         // TODO: comment out once DelegateClass is working
         // "android.view.View#isInEditMode",
         // "android.content.res.Resources$Theme#obtainStyledAttributes",
diff --git a/create/src/com/android/tools/layoutlib/create/Main.java b/create/src/com/android/tools/layoutlib/create/Main.java
index 43f2971..ce48069 100644
--- a/create/src/com/android/tools/layoutlib/create/Main.java
+++ b/create/src/com/android/tools/layoutlib/create/Main.java
@@ -66,7 +66,10 @@
             AsmGenerator agen = new AsmGenerator(log, osDestJar[0], new CreateInfo());
 
             AsmAnalyzer aa = new AsmAnalyzer(log, osJarPath, agen,
-                    new String[] { "android.view.View" },   // derived from
+                    new String[] {                          // derived from
+                        "android.view.View",
+                        "android.app.Fragment"
+                    },
                     new String[] {                          // include classes
                         "android.*", // for android.R
                         "android.util.*",