Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7f775243 authored by Xavier Ducrohet's avatar Xavier Ducrohet
Browse files

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
parent c9f32f23
Loading
Loading
Loading
Loading
+101 −0
Original line number Diff line number Diff line
/*
 * 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);
        }
    }
}
+14 −6
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import com.android.ninepatch.NinePatch;
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 @@ public final class Bridge implements ILayoutBridge {

        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 @@ public final class Bridge implements ILayoutBridge {
                    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 @@ public final class Bridge implements ILayoutBridge {
                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);
+15 −1
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ import com.android.layoutlib.api.IProjectCallback;
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.os.Looper;
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 @@ import java.util.Map.Entry;
/**
 * 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 @@ public final class BridgeContext extends Context {
        mProjectResources = projectResources;
        mFrameworkResources = frameworkResources;
        mStyleInheritanceMap = styleInheritanceMap;

        mFragments.mCurState = Fragment.CREATED;
        mFragments.mActivity = this;
    }

    public void setBridgeInflater(BridgeInflater inflater) {
@@ -155,6 +161,14 @@ public final class BridgeContext extends Context {
        return mLogger;
    }


    // ------------- Activity Methods

    @Override
    public LayoutInflater getLayoutInflater() {
        return mInflater;
    }

    // ------------ Context methods

    @Override
+1 −0
Original line number Diff line number Diff line
@@ -94,6 +94,7 @@ public final class CreateInfo implements ICreateInfo {
     * 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",
+4 −1
Original line number Diff line number Diff line
@@ -66,7 +66,10 @@ public class Main {
            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.*",