diff options
10 files changed, 226 insertions, 36 deletions
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java index 56d5617..0224c73 100644 --- a/core/java/android/preference/Preference.java +++ b/core/java/android/preference/Preference.java @@ -215,7 +215,7 @@ public class Preference implements Comparable<Preference> { final TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.Preference, defStyleAttr, defStyleRes); - for (int i = a.getIndexCount(); i >= 0; i--) { + for (int i = a.getIndexCount() - 1; i >= 0; i--) { int attr = a.getIndex(i); switch (attr) { case com.android.internal.R.styleable.Preference_icon: diff --git a/core/java/android/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java index ad940c6..0a0e625 100644 --- a/core/java/android/preference/PreferenceManager.java +++ b/core/java/android/preference/PreferenceManager.java @@ -156,7 +156,7 @@ public class PreferenceManager { * should be used ANY time a preference will be displayed, since some preference * types need an Activity for managed queries. */ - private PreferenceManager(Context context) { + /*package*/ PreferenceManager(Context context) { init(context); } diff --git a/tools/layoutlib/bridge/src/android/preference/BridgePreferenceInflater.java b/tools/layoutlib/bridge/src/android/preference/BridgePreferenceInflater.java new file mode 100644 index 0000000..c2122f4 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/preference/BridgePreferenceInflater.java @@ -0,0 +1,51 @@ +/* + * 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 android.preference; + +import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; + +import android.content.Context; +import android.util.AttributeSet; + +import java.util.Map; + +public class BridgePreferenceInflater extends PreferenceInflater { + + private final Map<Preference, Object> mViewCookieMap; + + public BridgePreferenceInflater(Context context, PreferenceManager preferenceManager, + Map<Preference, Object> viewCookieMap) { + super(context, preferenceManager); + mViewCookieMap = viewCookieMap; + } + + @Override + protected Preference onCreateItem(String name, AttributeSet attrs) + throws ClassNotFoundException { + Object viewKey; + if (attrs instanceof BridgeXmlBlockParser) { + viewKey = ((BridgeXmlBlockParser) attrs).getViewCookie(); + } else { + viewKey = null; + } + Preference preference = super.onCreateItem(name, attrs); + if (viewKey != null) { + mViewCookieMap.put(preference, viewKey); + } + return preference; + } +} diff --git a/tools/layoutlib/bridge/src/android/preference/Preference_Delegate.java b/tools/layoutlib/bridge/src/android/preference/Preference_Delegate.java new file mode 100644 index 0000000..230fe8c --- /dev/null +++ b/tools/layoutlib/bridge/src/android/preference/Preference_Delegate.java @@ -0,0 +1,94 @@ +/* + * 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 android.preference; + +import com.android.internal.R; +import com.android.layoutlib.bridge.android.BridgeContext; +import com.android.tools.layoutlib.annotations.LayoutlibDelegate; + +import org.xmlpull.v1.XmlPullParser; + +import android.content.Context; +import android.content.res.TypedArray; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ListView; + +import java.util.HashMap; +import java.util.Map; + +/** + * Delegate that provides implementation for native methods in {@link Preference} + * <p/> + * Through the layoutlib_create tool, selected methods of Preference have been replaced by calls to + * methods of the same name in this delegate class. + */ +public class Preference_Delegate { + + private static final Map<Preference, Object> sViewCookies = new HashMap<Preference, Object>(); + + @LayoutlibDelegate + /*package*/ static void dispatchSetInitialValue(Preference preference) { + // pass. + } + + @LayoutlibDelegate + /*package*/ static View getView(Preference pref, View convertView, ViewGroup parent) { + Context context = pref.getContext(); + BridgeContext bc = context instanceof BridgeContext ? ((BridgeContext) context) : null; + convertView = pref.getView_Original(convertView, parent); + Object cookie = sViewCookies.get(pref); + if (bc != null && cookie != null) { + bc.addViewKey(convertView, cookie); + } + return convertView; + } + + /** + * Inflates the parser and returns the ListView containing the Preferences. The caller must call + * {@link #clearCookiesMap()} when the rendering is complete. + */ + public static View inflatePreference(Context context, XmlPullParser parser, ViewGroup root) { + assert sViewCookies.isEmpty(); + PreferenceManager pm = new PreferenceManager(context); + PreferenceScreen ps = pm.getPreferenceScreen(); + PreferenceInflater inflater = new BridgePreferenceInflater(context, pm, sViewCookies); + ps = (PreferenceScreen) inflater.inflate(parser, ps, true); + ListView preferenceView = createContainerView(context, root); + ps.bind(preferenceView); + return preferenceView; + } + + private static ListView createContainerView(Context context, ViewGroup root) { + TypedArray a = context.obtainStyledAttributes(null, R.styleable.PreferenceFragment, + R.attr.preferenceFragmentStyle, 0); + int mLayoutResId = a.getResourceId(R.styleable.PreferenceFragment_layout, + R.layout.preference_list_fragment); + a.recycle(); + + LayoutInflater inflater = + (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + inflater.inflate(mLayoutResId, root, true); + + return (ListView) root.findViewById(android.R.id.list); + } + + public static void clearCookiesMap() { + sViewCookies.clear(); + } +} diff --git a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java index 5a467b2..b0d79a8 100644 --- a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java +++ b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java @@ -13,8 +13,8 @@ import javax.swing.text.Segment; /** * Delegate that provides implementation for native methods in {@link android.text.StaticLayout} - * - * Through the layoutlib_create tool, selected methods of Handler have been replaced + * <p/> + * Through the layoutlib_create tool, selected methods of StaticLayout have been replaced * by calls to methods of the same name in this delegate class. * */ diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 0af04ec..4d2c2fc 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -22,6 +22,7 @@ import static com.android.ide.common.rendering.api.Result.Status.SUCCESS; import com.android.annotations.NonNull; import com.android.ide.common.rendering.api.Capability; import com.android.ide.common.rendering.api.DrawableParams; +import com.android.ide.common.rendering.api.Features; import com.android.ide.common.rendering.api.LayoutLog; import com.android.ide.common.rendering.api.RenderSession; import com.android.ide.common.rendering.api.Result; @@ -180,7 +181,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { */ private static LayoutLog sCurrentLog = sDefaultLog; - private EnumSet<Capability> mCapabilities; + private static final int LAST_SUPPORTED_FEATURE = Features.PREFERENCES_RENDERING; @Override public int getApiLevel() { @@ -188,8 +189,16 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { } @Override + @Deprecated public EnumSet<Capability> getCapabilities() { - return mCapabilities; + // The Capability class is deprecated and frozen. All Capabilities enumerated there are + // supported by this version of LayoutLibrary. So, it's safe to use EnumSet.allOf() + return EnumSet.allOf(Capability.class); + } + + @Override + public boolean supports(int feature) { + return feature <= LAST_SUPPORTED_FEATURE; } @Override @@ -200,26 +209,6 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { sPlatformProperties = platformProperties; sEnumValueMap = enumValueMap; - // don't use EnumSet.allOf(), because the bridge doesn't come with its specific version - // of layoutlib_api. It is provided by the client which could have a more recent version - // with newer, unsupported capabilities. - mCapabilities = EnumSet.of( - Capability.UNBOUND_RENDERING, - Capability.CUSTOM_BACKGROUND_COLOR, - Capability.RENDER, - Capability.LAYOUT_ONLY, - Capability.EMBEDDED_LAYOUT, - Capability.VIEW_MANIPULATION, - Capability.PLAY_ANIMATION, - Capability.ANIMATED_VIEW_MANIPULATION, - Capability.ADAPTER_BINDING, - Capability.EXTENDED_VIEWINFO, - Capability.FIXED_SCALABLE_NINE_PATCH, - Capability.RTL, - Capability.ACTION_BAR, - Capability.SIMULATE_PLATFORM); - - BridgeAssetManager.initSystem(); // When DEBUG_LAYOUT is set and is not 0 or false, setup a default listener 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 new file mode 100644 index 0000000..e00ea6a --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/SessionParamsFlags.java @@ -0,0 +1,35 @@ +/* + * 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); + + // Disallow instances. + private SessionParamsFlags() {} +} 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 a2eed9a..c8e8ba7 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 @@ -49,6 +49,7 @@ import com.android.layoutlib.bridge.Bridge; 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.bars.Config; import com.android.layoutlib.bridge.bars.NavigationBar; import com.android.layoutlib.bridge.bars.StatusBar; @@ -73,6 +74,7 @@ import android.graphics.Bitmap; import android.graphics.Bitmap_Delegate; import android.graphics.Canvas; import android.graphics.drawable.Drawable; +import android.preference.Preference_Delegate; import android.util.DisplayMetrics; import android.util.TypedValue; import android.view.AttachInfo_Accessor; @@ -87,7 +89,6 @@ import android.view.ViewGroup.LayoutParams; import android.view.ViewGroup.MarginLayoutParams; import android.view.ViewParent; import android.view.WindowManagerGlobal_Delegate; -import android.view.ViewParent; import android.widget.AbsListView; import android.widget.AbsSpinner; import android.widget.ActionMenuView; @@ -397,7 +398,15 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { // it can instantiate the custom Fragment. Fragment_Delegate.setProjectCallback(params.getProjectCallback()); - View view = mInflater.inflate(mBlockParser, mContentRoot); + String rootTag = params.getFlag(SessionParamsFlags.FLAG_KEY_ROOT_TAG); + boolean isPreference = "PreferenceScreen".equals(rootTag); + View view; + if (isPreference) { + view = Preference_Delegate.inflatePreference(getContext(), mBlockParser, + mContentRoot); + } else { + view = mInflater.inflate(mBlockParser, mContentRoot); + } // done with the parser, pop it. context.popParser(); @@ -408,7 +417,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { AttachInfo_Accessor.setAttachInfo(mViewRoot); // post-inflate process. For now this supports TabHost/TabWidget - postInflateProcess(view, params.getProjectCallback()); + postInflateProcess(view, params.getProjectCallback(), isPreference ? view : null); // get the background drawable if (mWindowBackground != null) { @@ -590,6 +599,9 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { mSystemViewInfoList = visitAllChildren(mViewRoot, 0, params.getExtendedViewInfoMode(), false); + // clear the preferences cookie map. + Preference_Delegate.clearCookiesMap(); + // success! return SUCCESS.createResult(); } catch (Throwable e) { @@ -1210,12 +1222,16 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { * based on the content of the {@link FrameLayout}. * @param view the root view to process. * @param projectCallback callback to the project. + * @param skip the view and it's children are not processed. */ @SuppressWarnings("deprecation") // For the use of Pair - private void postInflateProcess(View view, IProjectCallback projectCallback) + private void postInflateProcess(View view, IProjectCallback projectCallback, View skip) throws PostInflateException { + if (view == skip) { + return; + } if (view instanceof TabHost) { - setupTabHost((TabHost)view, projectCallback); + setupTabHost((TabHost) view, projectCallback); } else if (view instanceof QuickContactBadge) { QuickContactBadge badge = (QuickContactBadge) view; badge.setImageToDefault(); @@ -1248,7 +1264,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { boolean skipCallbackParser = false; int count = binding.getHeaderCount(); - for (int i = 0 ; i < count ; i++) { + for (int i = 0; i < count; i++) { Pair<View, Boolean> pair = context.inflateView( binding.getHeaderAt(i), list, false /*attachToRoot*/, skipCallbackParser); @@ -1260,7 +1276,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } count = binding.getFooterCount(); - for (int i = 0 ; i < count ; i++) { + for (int i = 0; i < count; i++) { Pair<View, Boolean> pair = context.inflateView( binding.getFooterAt(i), list, false /*attachToRoot*/, skipCallbackParser); @@ -1289,11 +1305,11 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } } } else if (view instanceof ViewGroup) { - ViewGroup group = (ViewGroup)view; + ViewGroup group = (ViewGroup) view; final int count = group.getChildCount(); - for (int c = 0 ; c < count ; c++) { + for (int c = 0; c < count; c++) { View child = group.getChildAt(c); - postInflateProcess(child, projectCallback); + postInflateProcess(child, projectCallback, skip); } } } @@ -1361,6 +1377,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { for (int i = 0 ; i < count ; i++) { View child = content.getChildAt(i); String tabSpec = String.format("tab_spec%d", i+1); + @SuppressWarnings("ConstantConditions") // child cannot be null. int id = child.getId(); @SuppressWarnings("deprecation") Pair<ResourceType, String> resource = projectCallback.resolveResourceId(id); diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java index 500c338..2f04b11 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java @@ -152,6 +152,8 @@ public final class CreateInfo implements ICreateInfo { "android.graphics.Typeface#getSystemFontConfigLocation", "android.os.Handler#sendMessageAtTime", "android.os.HandlerThread#run", + "android.preference.Preference#dispatchSetInitialValue", + "android.preference.Preference#getView", "android.text.format.DateFormat#is24HourFormat", "android.util.Xml#newPullParser", "android.view.Choreographer#getRefreshRate", @@ -273,6 +275,7 @@ public final class CreateInfo implements ICreateInfo { private final static String[] EXCLUDED_CLASSES = new String[] { + "android.preference.PreferenceActivity", "org.kxml2.io.KXmlParser" }; diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java index cd3c39e..fa570c8 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java @@ -108,6 +108,7 @@ public class Main { "android.graphics.drawable.*", "android.content.*", "android.content.res.*", + "android.preference.*", "org.apache.harmony.xml.*", "com.android.internal.R**", "android.pim.*", // for datepicker |