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

Commit 4dfe4d43 authored by Jens Ole Lauridsen's avatar Jens Ole Lauridsen
Browse files

Support AppBar from Material Design.

This CL is a start for making the design able to render the AppBar.
We are still missing support for: system menu and the app icon.

Change-Id: I19600f8ee1e7e6492186a0b7ae7fb38c82e15c02
parent 8a265146
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ 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.impl.ParserFactory;
@@ -234,6 +235,13 @@ public final class BridgeInflater extends LayoutInflater {
            if (viewKey != null) {
                bc.addViewKey(view, viewKey);
            }
            String scrollPos = attrs.getAttributeValue(BridgeConstants.NS_RESOURCES, "scrollY");
            if (scrollPos != null) {
                if (scrollPos.endsWith("px")) {
                    int value = Integer.parseInt(scrollPos.substring(0, scrollPos.length() - 2));
                    bc.setScrollYPos(view, value);
                }
            }
        }
    }

+11 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import android.annotation.Nullable;
import android.annotation.NonNull;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -125,6 +126,7 @@ public final class BridgeContext extends Context {
    private final LayoutlibCallback mLayoutlibCallback;
    private final WindowManager mWindowManager;
    private final DisplayManager mDisplayManager;
    private final HashMap<View, Integer> mScrollYPos = new HashMap<View, Integer>();

    private Resources.Theme mTheme;

@@ -1738,4 +1740,13 @@ public final class BridgeContext extends Context {
        // pass
        return new File[0];
    }

    public void setScrollYPos(@NonNull View view, int scrollPos) {
        mScrollYPos.put(view, scrollPos);
    }

    public int getScrollYPos(@NonNull View view) {
        Integer pos = mScrollYPos.get(view);
        return pos != null ? pos : 0;
    }
}
+65 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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.LayoutLog;
import com.android.layoutlib.bridge.Bridge;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.view.View;

import java.lang.reflect.Method;

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 the design support lib.
 */
public class DesignLibUtil {

    private static final String PKG_PREFIX = "android.support.design.widget.";
    public static final String CN_COORDINATOR_LAYOUT = PKG_PREFIX + "CoordinatorLayout";
    public static final String CN_APPBAR_LAYOUT = PKG_PREFIX + "AppBarLayout";
    public static final String CN_COLLAPSING_TOOLBAR_LAYOUT =
            PKG_PREFIX + "CollapsingToolbarLayout";
    public static final String CN_TOOLBAR = "android.support.v7.widget.Toolbar";
    public static final int SCROLL_AXIS_VERTICAL = 1 << 1;

    /**
     * Tries to set the title of a view. This is used to set the title in a
     * CollapsingToolbarLayout.
     * <p/>
     * Any exceptions thrown during the process are logged in {@link Bridge#getLog()}
     */
    public static void setTitle(@NonNull View view, @Nullable String title) {
        if (title == null) {
            return;
        }
        try {
            Method setTitle = getMethod(view.getClass(), "setTitle", CharSequence.class);
            if (setTitle != null) {
                invoke(setTitle, view, title);
            }
        } catch (ReflectionException e) {
            Bridge.getLog().warning(LayoutLog.TAG_INFO,
                    "Error occurred while trying to set title.", e);
        }
    }
}
+98 −1
Original line number Diff line number Diff line
@@ -44,6 +44,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.RenderParamsFlags;
import com.android.layoutlib.bridge.android.support.DesignLibUtil;
import com.android.layoutlib.bridge.android.support.RecyclerViewUtil;
import com.android.layoutlib.bridge.bars.AppCompatActionBar;
import com.android.layoutlib.bridge.bars.BridgeActionBar;
@@ -148,7 +149,6 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
    private int mTitleBarSize;
    private int mActionBarSize;


    // information being returned through the API
    private BufferedImage mImage;
    private List<ViewInfo> mViewInfoList;
@@ -424,6 +424,8 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
            // post-inflate process. For now this supports TabHost/TabWidget
            postInflateProcess(view, params.getLayoutlibCallback(), isPreference ? view : null);

            setActiveToolbar(view, context, params);

            // get the background drawable
            if (mWindowBackground != null) {
                Drawable d = ResourceHelper.getDrawable(mWindowBackground, context);
@@ -544,6 +546,8 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
            // now do the layout.
            mViewRoot.layout(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);

            handleScrolling(mViewRoot);

            if (params.isLayoutOnly()) {
                // delete the canvas and image to reset them on the next full rendering
                mImage = null;
@@ -1349,6 +1353,99 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
        }
    }

    /**
     * If the root layout is a CoordinatorLayout with an AppBar:
     * Set the title of the AppBar to the title of the activity context.
     */
    private void setActiveToolbar(View view, BridgeContext context, SessionParams params) {
        View coordinatorLayout = findChildView(view, DesignLibUtil.CN_COORDINATOR_LAYOUT);
        if (coordinatorLayout == null) {
            return;
        }
        View appBar = findChildView(coordinatorLayout, DesignLibUtil.CN_APPBAR_LAYOUT);
        if (appBar == null) {
            return;
        }
        ViewGroup collapsingToolbar =
                (ViewGroup) findChildView(appBar, DesignLibUtil.CN_COLLAPSING_TOOLBAR_LAYOUT);
        if (collapsingToolbar == null) {
            return;
        }
        if (!hasToolbar(collapsingToolbar)) {
            return;
        }
        RenderResources res = context.getRenderResources();
        String title = params.getAppLabel();
        ResourceValue titleValue = res.findResValue(title, false);
        if (titleValue != null && titleValue.getValue() != null) {
            title = titleValue.getValue();
        }
        DesignLibUtil.setTitle(collapsingToolbar, title);
    }

    private View findChildView(View view, String className) {
        if (!(view instanceof ViewGroup)) {
            return null;
        }
        ViewGroup group = (ViewGroup) view;
        for (int i = 0; i < group.getChildCount(); i++) {
            if (isInstanceOf(group.getChildAt(i), className)) {
                return group.getChildAt(i);
            }
        }
        return null;
    }

    private boolean hasToolbar(View collapsingToolbar) {
        if (!(collapsingToolbar instanceof ViewGroup)) {
            return false;
        }
        ViewGroup group = (ViewGroup) collapsingToolbar;
        for (int i = 0; i < group.getChildCount(); i++) {
            if (isInstanceOf(group.getChildAt(i), DesignLibUtil.CN_TOOLBAR)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Set the vertical scroll position on all the components with the "scrollY" attribute. If the
     * component supports nested scrolling attempt that first, then use the unconsumed scroll part
     * to scroll the content in the component.
     */
    private void handleScrolling(View view) {
        BridgeContext context = getContext();
        int scrollPos = context.getScrollYPos(view);
        if (scrollPos != 0) {
            if (view.isNestedScrollingEnabled()) {
                int[] consumed = new int[2];
                if (view.startNestedScroll(DesignLibUtil.SCROLL_AXIS_VERTICAL)) {
                    view.dispatchNestedPreScroll(0, scrollPos, consumed, null);
                    view.dispatchNestedScroll(consumed[0], consumed[1], 0, scrollPos, null);
                    view.stopNestedScroll();
                    scrollPos -= consumed[1];
                }
            }
            if (scrollPos != 0) {
                view.scrollBy(0, scrollPos);
            } else {
                view.scrollBy(0, scrollPos);
            }
        } else {
            view.scrollBy(0, scrollPos);
        }

        if (!(view instanceof ViewGroup)) {
            return;
        }
        ViewGroup group = (ViewGroup) view;
        for (int i = 0; i < group.getChildCount(); i++) {
            View child = group.getChildAt(i);
            handleScrolling(child);
        }
    }

    /**
     * Check if the object is an instance of a class named {@code className}. This doesn't work
     * for interfaces.