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

Commit b556decf authored by Deepanshu Gupta's avatar Deepanshu Gupta
Browse files

LayoutLib: Switch SimpleDateFormat to icu.

Use ICU's implementation of SimpleDateFormat in LayoutLib. The format
patterns used are not supported by java.text.SimpleDateFormat.

The change required adding a mechanism for promoting visibility of
fields in the framework in the create tool.
TODO: Add additional tests in the create tool for this new
functionality.

Change-Id: Id0f4be41f9731c42a28919c32cc5ef271a656982
parent 82c7fdb1
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
<?xml version="1.0" encoding="UTF-8"?>
<component name="InspectionProjectProfileManager">
<component name="InspectionProjectProfileManager">
  <profile version="1.0" is_locked="false">
  <profile version="1.0" is_locked="false">
    <option name="myName" value="Project Default" />
    <option name="myName" value="Project Default" />
@@ -7,5 +8,6 @@
      <option name="CHECK_TRY_CATCH_SECTION" value="true" />
      <option name="CHECK_TRY_CATCH_SECTION" value="true" />
      <option name="CHECK_METHOD_BODY" value="true" />
      <option name="CHECK_METHOD_BODY" value="true" />
    </inspection_tool>
    </inspection_tool>
    <inspection_tool class="ToArrayCallWithZeroLengthArrayArgument" enabled="false" level="WARNING" enabled_by_default="false" />
  </profile>
  </profile>
</component>
</component>
 No newline at end of file
+99 −0
Original line number Original line 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 android.widget;

import com.android.tools.layoutlib.annotations.LayoutlibDelegate;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.icu.text.SimpleDateFormat;
import android.text.format.DateFormat;

import java.util.Calendar;
import java.util.Locale;

/**
 * Delegate that provides implementation for some methods in {@link SimpleMonthView}.
 * <p/>
 * Through the layoutlib_create tool, selected methods of SimpleMonthView have been replaced by
 * calls to methods of the same name in this delegate class.
 * <p/>
 * The main purpose of this class is to use {@link android.icu.text.SimpleDateFormat} instead of
 * {@link java.text.SimpleDateFormat}.
 */
public class SimpleMonthView_Delegate {

    private static final String DEFAULT_TITLE_FORMAT = "MMMMy";
    private static final String DAY_OF_WEEK_FORMAT = "EEEEE";

    // Maintain a cache of the last view used, so that the formatters can be reused.
    @Nullable private static SimpleMonthView sLastView;
    @Nullable private static SimpleMonthView_Delegate sLastDelegate;

    private SimpleDateFormat mTitleFormatter;
    private SimpleDateFormat mDayOfWeekFormatter;

    private Locale locale;

    @LayoutlibDelegate
    /*package*/ static CharSequence getTitle(SimpleMonthView view) {
        if (view.mTitle == null) {
            SimpleMonthView_Delegate delegate = getDelegate(view);
            if (delegate.mTitleFormatter == null) {
                delegate.mTitleFormatter = new SimpleDateFormat(DateFormat.getBestDateTimePattern(
                        getLocale(delegate, view), DEFAULT_TITLE_FORMAT));
            }
            view.mTitle = delegate.mTitleFormatter.format(view.mCalendar.getTime());
        }
        return view.mTitle;
    }

    @LayoutlibDelegate
    /*package*/ static String getDayOfWeekLabel(SimpleMonthView view, int dayOfWeek) {
        view.mDayOfWeekLabelCalendar.set(Calendar.DAY_OF_WEEK, dayOfWeek);
        SimpleMonthView_Delegate delegate = getDelegate(view);
        if (delegate.mDayOfWeekFormatter == null) {
            delegate.mDayOfWeekFormatter =
                    new SimpleDateFormat(DAY_OF_WEEK_FORMAT, getLocale(delegate, view));
        }
        return delegate.mDayOfWeekFormatter.format(view.mDayOfWeekLabelCalendar.getTime());
    }

    private static Locale getLocale(SimpleMonthView_Delegate delegate, SimpleMonthView view) {
        if (delegate.locale == null) {
            delegate.locale = view.getContext().getResources().getConfiguration().locale;
        }
        return delegate.locale;
    }

    @NonNull
    private static SimpleMonthView_Delegate getDelegate(SimpleMonthView view) {
        if (view == sLastView) {
            assert sLastDelegate != null;
            return sLastDelegate;
        } else {
            sLastView = view;
            sLastDelegate = new SimpleMonthView_Delegate();
            return sLastDelegate;
        }
    }

    public static void clearCache() {
        sLastView = null;
        sLastDelegate = null;
    }
}
+2 −0
Original line number Original line Diff line number Diff line
@@ -36,6 +36,7 @@ import android.util.DisplayMetrics;
import android.view.ViewConfiguration_Accessor;
import android.view.ViewConfiguration_Accessor;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodManager_Accessor;
import android.view.inputmethod.InputMethodManager_Accessor;
import android.widget.SimpleMonthView_Delegate;


import java.util.Locale;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeUnit;
@@ -277,6 +278,7 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso
            mContext.getRenderResources().setLogger(null);
            mContext.getRenderResources().setLogger(null);
        }
        }
        ParserFactory.setParserFactory(null);
        ParserFactory.setParserFactory(null);
        SimpleMonthView_Delegate.clearCache();
    }
    }


    public static BridgeContext getCurrentContext() {
    public static BridgeContext getCurrentContext() {
+32 −14
Original line number Original line Diff line number Diff line
@@ -77,6 +77,8 @@ public class AsmGenerator {
    /** Methods to inject. FQCN of class in which method should be injected => runnable that does
    /** Methods to inject. FQCN of class in which method should be injected => runnable that does
     * the injection. */
     * the injection. */
    private final Map<String, ICreateInfo.InjectMethodRunnable> mInjectedMethodsMap;
    private final Map<String, ICreateInfo.InjectMethodRunnable> mInjectedMethodsMap;
    /** A map { FQCN => set { field names } } which should be promoted to public visibility */
    private final Map<String, Set<String>> mPromotedFields;


    /**
    /**
     * Creates a new generator that can generate the output JAR with the stubbed classes.
     * Creates a new generator that can generate the output JAR with the stubbed classes.
@@ -109,20 +111,8 @@ public class AsmGenerator {


        // Create the map/set of methods to change to delegates
        // Create the map/set of methods to change to delegates
        mDelegateMethods = new HashMap<String, Set<String>>();
        mDelegateMethods = new HashMap<String, Set<String>>();
        for (String signature : createInfo.getDelegateMethods()) {
        addToMap(createInfo.getDelegateMethods(), mDelegateMethods);
            int pos = signature.indexOf('#');

            if (pos <= 0 || pos >= signature.length() - 1) {
                continue;
            }
            String className = binaryToInternalClassName(signature.substring(0, pos));
            String methodName = signature.substring(pos + 1);
            Set<String> methods = mDelegateMethods.get(className);
            if (methods == null) {
                methods = new HashSet<String>();
                mDelegateMethods.put(className, methods);
            }
            methods.add(methodName);
        }
        for (String className : createInfo.getDelegateClassNatives()) {
        for (String className : createInfo.getDelegateClassNatives()) {
            className = binaryToInternalClassName(className);
            className = binaryToInternalClassName(className);
            Set<String> methods = mDelegateMethods.get(className);
            Set<String> methods = mDelegateMethods.get(className);
@@ -187,9 +177,33 @@ public class AsmGenerator {
            returnTypes.add(binaryToInternalClassName(className));
            returnTypes.add(binaryToInternalClassName(className));
        }
        }


        mPromotedFields = new HashMap<String, Set<String>>();
        addToMap(createInfo.getPromotedFields(), mPromotedFields);

        mInjectedMethodsMap = createInfo.getInjectedMethodsMap();
        mInjectedMethodsMap = createInfo.getInjectedMethodsMap();
    }
    }


    /**
     * For each value in the array, split the value on '#' and add the parts to the map as key
     * and value.
     */
    private void addToMap(String[] entries, Map<String, Set<String>> map) {
        for (String entry : entries) {
            int pos = entry.indexOf('#');
            if (pos <= 0 || pos >= entry.length() - 1) {
                return;
            }
            String className = binaryToInternalClassName(entry.substring(0, pos));
            String methodOrFieldName = entry.substring(pos + 1);
            Set<String> set = map.get(className);
            if (set == null) {
                set = new HashSet<String>();
                map.put(className, set);
            }
            set.add(methodOrFieldName);
        }
    }

    /**
    /**
     * Returns the list of classes that have not been renamed yet.
     * Returns the list of classes that have not been renamed yet.
     * <p/>
     * <p/>
@@ -380,6 +394,10 @@ public class AsmGenerator {
            }
            }
        }
        }


        Set<String> promoteFields = mPromotedFields.get(className);
        if (promoteFields != null && !promoteFields.isEmpty()) {
            cv = new PromoteFieldClassAdapter(cv, promoteFields);
        }
        cr.accept(cv, 0);
        cr.accept(cv, 0);
        return cw.toByteArray();
        return cw.toByteArray();
    }
    }
+13 −0
Original line number Original line Diff line number Diff line
@@ -119,6 +119,11 @@ public final class CreateInfo implements ICreateInfo {
        return excludedClasses;
        return excludedClasses;
    }
    }


    @Override
    public String[] getPromotedFields() {
        return PROMOTED_FIELDS;
    }

    @Override
    @Override
    public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
    public Map<String, InjectMethodRunnable> getInjectedMethodsMap() {
        return INJECTED_METHODS;
        return INJECTED_METHODS;
@@ -185,6 +190,8 @@ public final class CreateInfo implements ICreateInfo {
        "android.view.RenderNode#nSetElevation",
        "android.view.RenderNode#nSetElevation",
        "android.view.RenderNode#nGetElevation",
        "android.view.RenderNode#nGetElevation",
        "android.view.ViewGroup#drawChild",
        "android.view.ViewGroup#drawChild",
        "android.widget.SimpleMonthView#getTitle",
        "android.widget.SimpleMonthView#getDayOfWeekLabel",
        "android.widget.TimePickerClockDelegate#getAmOrPmKeyCode",
        "android.widget.TimePickerClockDelegate#getAmOrPmKeyCode",
        "com.android.internal.view.menu.MenuBuilder#createNewMenuItem",
        "com.android.internal.view.menu.MenuBuilder#createNewMenuItem",
        "com.android.internal.util.XmlUtils#convertValueToInt",
        "com.android.internal.util.XmlUtils#convertValueToInt",
@@ -289,6 +296,12 @@ public final class CreateInfo implements ICreateInfo {
            "org.kxml2.io.KXmlParser"
            "org.kxml2.io.KXmlParser"
        };
        };


    private final static String[] PROMOTED_FIELDS = new String[] {
        "android.widget.SimpleMonthView#mTitle",
        "android.widget.SimpleMonthView#mCalendar",
        "android.widget.SimpleMonthView#mDayOfWeekLabelCalendar"
    };

    /**
    /**
     * List of classes for which the methods returning them should be deleted.
     * List of classes for which the methods returning them should be deleted.
     * The array contains a list of null terminated section starting with the name of the class
     * The array contains a list of null terminated section starting with the name of the class
Loading